今回はコレクション (java.util.Collection) について。 Groovy JDK を参考にしています(一覧)。
- 作者: Dierk Konig,Andrew Glover,Paul King,Guillaume Laforge,Jon Skeet,杉浦孝,櫻井正樹,須江信洋,関谷和愛,佐野徹郎,寺沢尚史
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2008/09/27
- メディア: 単行本(ソフトカバー)
- 購入: 5人 クリック: 146回
- この商品を含むブログ (121件) を見る
今回は、Groovy で Collection に施された拡張(追加されたメソッド)を見ていきます。 メソッドの振る舞い等は載せてません。 説明に関しては「Sample is Best」 だと思うので、あれこれサンプルコードを書きましたが、書いてたら結構量が増えてシンプルにはなりませんでした・・・
以下、表中にメソッドを Generics にしたものを書いていますが、これらは Collection<E> オブジェクトに対するものです。 ただし、仕様などではなく、正確でもありません(<? extends E> などを単純に <E> と書いたりしています)。
クロージャに関しては
- it の型が E
- 返り値が R
のとき、Closuer<E, R> と書いてます。
boolean を返すメソッド
メソッド名 | 返り値 | メソッド名 (Generics) |
返り値 (Generics) |
|
---|---|---|---|---|
asBoolean()** | boolean | |||
isCase(Object)** | boolean | |||
disjoint(Collection) | boolean | disjoint(Collection<E>) | boolean | |
any()* any(Closure)* |
boolean | any() any(Closure<E, boolean>) |
boolean | |
every()* every(Closure)* |
boolean | every() every(Closure<E, boolean>) |
boolean |
- *付きは Object に定義されているメソッドです。
- **付きは Object に定義されているメソッドをオーバーライドしています。
any は論理和、every は論理積ですかね。 disjoint は2つの集合が排反かどうかを検証します。
//***** disjoint(Collection) ***** list1 = (0..9) as List; list2 = ('a'..'z') as List; assert list1.disjoint(list2); //***** any(), any(Closure) ***** list = [false, 1, null, '2']; assert list.any(); result = list.any{ it != null }; // boolean を返すクロージャ assert result; //***** every(), every(Closure) ***** assert list.every() == false; result = list.every{ it != null }; // boolean を返すクロージャ assert result == false;
値オブジェクトを返すメソッド
数値や文字列を返すメソッド(asType はちょっと違うかな?)。 なんか、Visitor の代わりにクロージャを使って情報をかき集めるような処理。
メソッド名 | 返り値 | メソッド名 (Generics) |
返り値 (Generics) |
|
---|---|---|---|---|
asType(Class)** | Object | asType(Class<T>) | T | |
count(Object) | Number | count(E) | Number | |
join(String) | String | |||
toListString() | String | |||
inject(Object, Closure)** | Object | inject(R, Closure<R, E, R>) | R | |
sum() sum(Object) sum(Closure) sum(Object, Closure) |
Object | sum() sum(E) sum(Closure<E, R>) sum(R, Closure<E, R>) |
E E R R |
- *付きは Object に定義されているメソッドです。
- **付きは Object に定義されているメソッドをオーバーライドしています。
//***** asType(Class) ***** list = (1..9) as List; //***** count(Object) ***** assert list.count(5) == 1; //***** join(String) ***** assert list.join('-') == '1-2-3-4-5-6-7-8-9'; //***** toListString() ***** assert list.toListString() == '[1, 2, 3, 4, 5, 6, 7, 8, 9]'; //***** inject(Closure) ***** prod = 100; prod = list.inject(prod) { prod, i -> prod * i }; // Object を返すクロージャ assert prod == 100*1*2*3*4*5*6*7*8*9; //***** sum() ***** assert list.sum() == 1+2+3+4+5+6+7+8+9; assert list.sum(100) == 100+1+2+3+4+5+6+7+8+9; result = list.sum { it * it }; // Object を返すクロージャ assert result == 1*1 + 2*2 + 3*3 + 4*4 + 5*5 + 6*6 + 7*7 + 8*8 + 9*9;
要素を返すメソッド
指定した条件を満たすようなコレクションの要素を取得するメソッド。 条件を満たす要素が複数ある場合は、最初に見つかったものを返すと思います。
メソッド名 | 返り値 | メソッド名 (Generics) |
返り値 (Generics) |
|
---|---|---|---|---|
find(Closure) | Object | find(Closure<E, boolean>) | E | |
max() max(Comparator) max(Closure) |
Object | max() max(Comparator<E>) max(Closure<E, Comparable<?>>) max(Closure<E, E, int>) |
E | |
min() min(Comparator) min(Closure) |
Object | min() min(Comparator<E>) min(Closure<E, Comparable<?>>) min(Closure<E, E, int>) |
E |
list = (1..20) as List; //***** find() ***** result = list.find { it % 7 == 0 } // boolean を返すクロージャ assert result == 7; //***** max(), min() ***** assert list.max() == 20; assert list.min() == 1; //***** max(Closure), min(Closure) ***** result = list.max { it % 5}; // Comparable を返すクロージャ assert result == 4; // 最初の最大値 result = list.min { a, b -> (a % 5) - (b % 5) }; // int を返すクロージャ assert result == 5; // 最初の最小値
Collection 返すメソッド
メソッド名 | 返り値 | メソッド名 (Generics) |
返り値 (Generics) |
|
---|---|---|---|---|
asImmutable() | Collection | Collection<E> | ||
asSynchronized() | Collection | Collection<E> | ||
each(Closure)* | Object | each(Closure<E, ?>) | Collection<E> | |
eachWithIndex(Closure)* | Object | eachWithIndex(Closure<E, int, ?>) | Collection<E> | |
intersect(Collection) | Collection | intersect(Collection<E>) | Collection<E> | |
findAll(Closure)** | Collection | findAll(Closure<E, R>) | Collection<R> | |
collect(Collection, Closure)** | Collection | collect(Collection<R>, Closure<E, R>) | Collection<R> | |
unique() unique(Comparator) unique(Closure) |
Collection | unique() unique(Comparator<?>) unique(Closure<E, ?>) unique(Closure<E, E, int>) |
Collection<E> | |
grep(Object)* | Collection | grep(Object) | Collection<E> |
- *付きは Object に定義されているメソッドです。
- **付きは Object に定義されているメソッドをオーバーライドしています。
each(), eachWithIndex() は、通常、返り値の Object (実際は Collection)は使わず、Closure の中で処理を行うのに使います。
intersect() は2つの集合の共通部分。
unique() はダブった要素を1つにまとめます。 クロージャを引数にとるメソッドは、「ダブる」とは何かを指定します。 数学でいう、商集合を作る手続きと同じですかね。 「ダブる」とは何かを指定するクロージャが商集合を作る際の同値関係を指定してるってことで。
//***** intersect(Collection) ***** list1 = (1..20) as List; list2 = (18..30) as List; assert list1.intersect(list2) == [18, 19, 20]; //***** each(Closure), eachWithIndex(Closure) ***** list = ('a'..'e') as LinkedList; str = ''; result = list.each{ str += it }; assert str == 'abcde'; assert result == ['a', 'b', 'c', 'd', 'e']; // 通常、返り値は使わない assert result instanceof LinkedList; list = ('a'..'e') as List; str = ''; result = list.eachWithIndex{ it, i -> str += it+i+' ' }; assert str == 'a0 b1 c2 d3 e4 '; assert result == ['a', 'b', 'c', 'd', 'e']; //***** findAll(Closure) ***** list = (1..10) as List; result = list.findAll { it % 3 == 0 }; // boolean を返すクロージャ assert result == [3, 6, 9] //***** collect(Collection, Closure) ***** list = [1, 2, 3]; result = [0] as LinkedList; result = list.collect(result){ it * it }; // Object を返すクロージャ assert result == [0, 1, 4, 9]; assert result instanceof LinkedList; //***** unique() ***** assert [1, 3, 3].unique() == [1, 3]; //***** unique(Closure) ***** list = (1..10).toList(); result = list.unique { it % 3 }; // int を返すクロージャ assert result == [1, 2, 3]; list = (1..10).toList(); result = list.unique { a, b -> (a - b) % 3 }; // Object (Comparable) を返すクロージャ assert result == [1, 2, 3]; //***** grep() ***** list = (1..10) as List; filter = 2..5; assert list.grep(filter) == [2, 3, 4, 5];
List を返すメソッド
メソッド名 | 返り値 | メソッド名 (Generics) |
返り値 (Generics) |
|
---|---|---|---|---|
asList() | List | asList() | List<E> | |
toList() | List | toList() | List<E> | |
getAt(String)** | List | getAt(String) | List<E> | |
multiply(Number) | List | multiply(Number) | List<E> | |
collect(Closure)** | List | collect(Closure<E, R>) | List<R> | |
sort() sort(Comparator) sort(Closure) |
List | sort() sort(Comparator<E>) sort(Closure<E, R>) |
List<E> List<E> List<R> |
- *付きは Object に定義されているメソッドです。
- **付きは Object に定義されているメソッドをオーバーライドしています。
//***** getAt(String) ***** list = [1, 'x', 2.0]; assert list.getAt('class') == [Integer.class, String.class, BigDecimal.class]; //***** multiply(Number) ***** list = [1, 2]; assert list.multiply(3) == [1, 2, 1, 2, 1, 2]; assert list * 3 == [1, 2, 1, 2, 1, 2]; // multiply() は * 演算子の実装 assert (list * 3).size() == list.size() * 3; //***** collect(Closure) ***** list = [1, 2, 3]; result = list.collect { it * it }; // Object を返すクロージャ assert result == [1, 4, 9]; //***** sort(), sort(Closure) ***** assert [3, 1, 2].sort() == [1, 2, 3]; list = ['abc', 'de', 'fghi', 'j']; result = list.sort { it.size() }; // Comparable を返すクロージャ result = ['j', 'de', 'abc', 'fghi']; list = ['abc', 'de', 'fghi', 'j']; result = list.sort { a, b -> a.size() <=> b.size() }; // int を返すクロージャ result = ['j', 'de', 'abc', 'fghi'];
メタ・コレクションに関するメソッド
「配列の配列」は多重配列というので、「コレクションのコレクション」は多重コレクションと言うのでしょうか? どのくらい一般的なことかは分かりませんが、「○○の○○」をメタ○○というので*1、「コレクションのコレクション」をメタ・コレクションと勝手に呼ぶことにします*2。
ここでは、メタ・コレクションに対してのメソッドか、呼び出した返り値がメタ・コレクションになるメソッドを見ていきます。
メソッド名 | 返り値 | メソッド名 (Generics) |
返り値 (Generics) |
|
---|---|---|---|---|
collectAll(Closure) | List | collectAll(Closure<E, R>)+ | 下記参照++ | |
collectAll(Collection, Closure) | Collection | collectAll(Collection<R>, Closure<E, R>)+ | 下記参照++ | |
split(Closure)** | Collection*** | split(Closure<E, boolean>) | List<Collection<E>> | |
groupBy(Closure) | Map | groupBy(Closure<E, R>) | Map<R, Collection<E>> | |
flatten() flatten(Closure) |
Collection | flatten()+ flatten(Closure<E, R>)+ |
Collection<E> Collection<R> |
|
combinations() | List | combinations() | List<E> | |
eachPermutation(Closure) | Iterator | eachPermutation(Closure<E, ?>) |
- *付きは Object に定義されているメソッドです。
- **付きは Object に定義されているメソッドをオーバーライドしています。
- ***split(Closure) の返り値は Collection となっていますが、常に(2要素の) List であると思われます(順序が決まっている)。 型階層に矛盾はありませんが。
- +付きのメソッドは、任意の階層構造のコレクションに対して呼び出せます。
- ++付き (collectAll) はメソッドを呼び出されたオブジェクトの階層構造を保存します。
- combinations() メソッドは、Collection に対して呼び出すなら、返り値も Collection でないといけない気がしますが・・・
//***** collectAll(Closure) ***** list = [[1, 2, 3], [4], [5, 6]]; result = list.collectAll { it * it }; // Object を返すクロージャ assert result == [[1, 4, 9], [16], [25, 36]]; //***** collectAll(Collection, Closure) ***** list = [1, [2, 3], [4], []]; result = ['0'] as LinkedList; result = list.collectAll(result){ it.toString() }; // Object を返すクロージャ assert result == ['0', '1', ['2', '3'], ['4'], []]; assert result instanceof LinkedList; //***** split(Closure) ***** list = (1..6) as List; result = list.split { it % 3 == 0 }; // boolean を返すクロージャ(真偽で分類) assert result == [[3, 6], [1, 2, 4, 5]]; assert result.size() == 2; assert result[0].size() + result[1].size() == list.size(); //***** groupBy() ***** list = (1..6) as List; map = list.groupBy { it % 3 }; // Number を返すクロージャ(返り値の値で分類) assert map == [0:[3, 6], 1:[1, 4], 2:[2, 5]]; //***** flatten() ***** list = [[0, 1], [2, 3]]; assert list.flatten() == [0, 1, 2, 3]; list = [['a', 'b'], 'cd', 'efg']; result = list.flatten { it.toList() }; // Object を返すクロージャ(配列、コレクション以外の要素の処理の仕方) assert result == ['a', 'b', 'c', 'd', 'e', 'f', 'g']; //***** combinations() ***** list = [['a', 'b'], ['1', '2']]; assert list.combinations() == [['a', '1'], ['b', '1'], ['a', '2'], ['b', '2']]; assert list.combinations().size() == list[0].size() * list[1].size(); assert list.combinations().size() == list.inject(1){n, inList -> n * inList.size()}; //***** eachPermutation() ***** list = ['a', 'b', 'c']; result = new LinkedList(); list.eachPermutation { result.add(it) }; assert result == [['a', 'b', 'c'], ['a', 'c', 'b'], ['b', 'a', 'c'], ['b', 'c', 'a'], ['c', 'a', 'b'], ['c', 'b', 'a']]; n = 1; 1.upto(list.size()){ n *= it }; assert result.size() == n;