Eclipse CollectionsはJDKのコレクションやStream APIには存在しないさまざまな機能を豊富に備えています。Eclipse Collectionsの2つ目の利点は、この豊富な機能にあると言えます。本連載では各機能を紹介していきますが、ここでは例としてデフォルトのJDKには存在しない便利機能を3つ紹介します。
対になる2つのコレクションの要素をペアにして1つのコレクションとして保持したい場合に便利な機能です。下の例では、日本語のリストと英語のリストを元に、日本語と英語が対になったリストを作成しています。
ImmutableList<String> japaneseNumbers = Lists.immutable.of("いち", "に", "さん");
ImmutableList<String> englishNumbers = Lists.immutable.of("One", "Two", "Three");
ImmutableList<Pair<String, String>> zip = japaneseNumbers.zip(englishNumbers);
System.out.println(zip);
// [いち:One, に:Two, さん:Three]
ここで、「Pair」というのは2つの要素を持つデータ構造で、このケースでは日本語と英語2つの文字列を保持する目的で使用しています。ちなみに2つのコレクションのサイズが一致しない場合は、サイズが小さい方のコレクションに合わせて処理され、残りの要素は無視されます。
1つのコレクションから、指定したサイズで分割した新たなコレクションを作成します。下記の例では、10個の要素を持つリストをサイズが2のコレクションにそれぞれ分割しています。
ImmutableList<String> oneToTen =
Lists.immutable.of("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten");
RichIterable<RichIterable<String>> chunk = oneToTen.chunk(2);
System.out.println(chunk);
// [[One, Two], [Three, Four], [Five, Six], [Seven, Eight], [Nine, Ten]]
大量のデータを一定サイズに分割したいときなどに使えます。
コレクションを最初から走査し、条件に合致する限り要素を取ってきます。条件に合わない要素において処理を停止します。下記の例ではOneからTenまでの文字列リストに対し、“S”で始まらない文字列を取ってきています。最初に“Six”で条件に合致しなくなるので、その手前の“Five”までが新たなコレクションとして返ってきます。
ImmutableList<String> oneToTen =
Lists.immutable.of("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten");
ImmutableList<String> oneToFive =
oneToTen.takeWhile(number -> !number.startsWith("S"));
System.out.println(oneToFive);
// [One, Two, Three, Four, Five]
以上3つの簡単な例を挙げましたが、Eclipse Collectionsには便利機能が他にも多数備わっています。Eclipse Collectionsのほとんどのコレクションクラスが実装している「RichIterable」というインタフェースには200を超えるメソッドが存在しています。コレクションを扱う際に、「こういうことがしたいな」と思うことが大抵備わっているので、使っていて非常に快適なライブラリかと思います。もしやりたい機能が現在Eclipse Collectionsで提供されていなくても、OSSですので自ら開発してコントリビュートすることも可能なのです。
Eclipse Collectionsはコレクションフレームワークですので、コレクションの種類そのものも豊富です。Javaの開発者の皆さんにとっては、ListやSet、MapなどJDKで提供されているコレクションはなじみ深いと思いますが、Eclipse Collectionsではさらにさまざまなコレクション型が提供されており、ユースケースに応じて活用できます。ここでは3つのコレクション型と、不変コレクション型を紹介します。
Bagは要素の出現回数を保持しておくのに便利なデータ構造です。例えばショッピングカートで商品の個数を保持するような機能として使えます。
MutableBag<String> bag = Bags.mutable.empty();
bag.add("Apple");
bag.add("Apple");
bag.add("Apple");
bag.add("Banana");
bag.addOccurrences("Orange", 2); //出現回数を直接加えることもできます
System.out.println(bag.occurrencesOf("Apple")); // 3
System.out.println(bag.occurrencesOf("Banana")); // 1
System.out.println(bag.occurrencesOf("Orange")); // 2
BiMapはMapと似たデータ構造ですが、KeyとValueを入れ替えたMapデータ構造をinverse()メソッドを通じて取得できます。BiMapではValueもKeyの役割を果たすため、Valueも重複がないようにする必要があります。
ImmutableBiMap<String, String> idToStudentMap =
BiMaps.immutable.of(
"A123456", "山田一郎",
"A234567", "佐藤二郎",
"B345678", "田中三郎",
"B987654", "鈴木四郎");
System.out.println(idToStudentMap.get("A234567"));
// 佐藤二郎
System.out.println(idToStudentMap.inverse().get("田中三郎"));
// B345678
上記の例では、学籍番号と学生の名前のBiMapを双方向に扱っています。
MapはKey-Valueの対を保持するデータ構造ですが、Multimapは、MapのValueに相当する部分がコレクションの際に便利に扱えるデータ構造です。ここではgroupByというAPIと併せて見てみましょう。
MutableList<String> oneToTen =
Lists.mutable.of("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten");
MutableListMultimap<String, String> groupByFirstString =
oneToTen.groupBy(s -> s.substring(0, 1));
System.out.println(groupByFirstString);
// {T=[Two, Three, Ten], E=[Eight], F=[Four, Five], S=[Six, Seven], N=[Nine], O=[One]}
上記のコードでは、OneからTenの文字列のリストを、「groupBy」というAPIを使って1文字目の文字でグルーピングしています。グルーピングの結果は「MutableListMultimap<String, String>」というMultimapに格納されています。これは、Valueをリストとして持つ可変なMultimapです。出力結果を見ると、例えば“T”というKeyに対して[Two, Three, Ten]というリストのValueを持っているのが分かります。
Multimapの利点の1つとして、Valueがコレクションであることを気にすることなく自然にKeyとValue要素を追加できることが挙げられます。
groupByFirstString.put("Z", "Zero");
groupByFirstString.put("E", "Eleven");
System.out.println(groupByFirstString);
// {T=[Two, Three, Ten], E=[Eight, Eleven], F=[Four, Five], S=[Six, Seven], N=[Nine], O=[One], Z=[Zero]}
上記の例では、”Z”に対応するKey-Valueはもともと存在していませんでしたがput("Z", "Zero")を呼ぶだけで[Zero]というListに格納されたValueを作成してくれています。また、既に存在している“E”のKeyに対しては、もともとのリストに追加して[Eight, Eleven]となっています。
同じことをMap<String, List<String>>で実現しようとすると、既にKeyが存在するのかどうかの判別や、存在しなかった際のListの初期化など、煩雑な処理を自分で書かなければなりません。同様の処理を今まで自分で実装していた開発者にとっても、Multimapが選択肢の1つとなるかと思います。
Eclipse Collectionsでは、全てのデータ構造に対して不変なコレクション(Immutable)と可変なコレクション(Mutable)が用意されています。不変なコレクションは追加、削除、更新用のAPIが存在しないため、作成後に変更されることがありません。常に同じ要素を持つことが保証されているため安全に扱うことができ、スレッドセーフでもあります。
ImmutableList<String> oneToFive =
Lists.immutable.of("One", "Two", "Three", "Four", "Five");
oneToFive.add("Six"); // コンパイルエラー!
コレクションを防御的に扱いたい際にはImmutableコレクションを使ってみましょう。
初心者のためのJavaラムダ式入門とJDKのインストール、IDEの環境構築
キュー構造をJavaで実装してジェネリック型を理解する
コレクションフレームワークを拡張するCollectionsCopyright © ITmedia, Inc. All Rights Reserved.