ゴールドマン・サックス発のJavaコレクションフレームワーク、その7つの特徴と歴史とはコレクション処理の万能道具箱Eclipse Collections入門(1)(2/3 ページ)

» 2016年08月05日 05時00分 公開
[伊藤博志ゴールドマン・サックス]

【2】コレクション処理の機能の豊富さ

 Eclipse CollectionsはJDKのコレクションやStream APIには存在しないさまざまな機能を豊富に備えています。Eclipse Collectionsの2つ目の利点は、この豊富な機能にあると言えます。本連載では各機能を紹介していきますが、ここでは例としてデフォルトのJDKには存在しない便利機能を3つ紹介します。

zip処理

 対になる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つのコレクションのサイズが一致しない場合は、サイズが小さい方のコレクションに合わせて処理され、残りの要素は無視されます。

zip処理

chunk処理

 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]]

 大量のデータを一定サイズに分割したいときなどに使えます。

chunk処理

take-while処理

 コレクションを最初から走査し、条件に合致する限り要素を取ってきます。条件に合わない要素において処理を停止します。下記の例では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]
take-while処理

 以上3つの簡単な例を挙げましたが、Eclipse Collectionsには便利機能が他にも多数備わっています。Eclipse Collectionsのほとんどのコレクションクラスが実装している「RichIterable」というインタフェースには200を超えるメソッドが存在しています。コレクションを扱う際に、「こういうことがしたいな」と思うことが大抵備わっているので、使っていて非常に快適なライブラリかと思います。もしやりたい機能が現在Eclipse Collectionsで提供されていなくても、OSSですので自ら開発してコントリビュートすることも可能なのです。

【3】JDKに存在しないさまざまなコレクション型

 Eclipse Collectionsはコレクションフレームワークですので、コレクションの種類そのものも豊富です。Javaの開発者の皆さんにとっては、ListやSet、MapなどJDKで提供されているコレクションはなじみ深いと思いますが、Eclipse Collectionsではさらにさまざまなコレクション型が提供されており、ユースケースに応じて活用できます。ここでは3つのコレクション型と、不変コレクション型を紹介します。

Bag:要素の出現回数を保持するコレクション

 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
Bag

BiMap:Key-Valueを双方向で扱えるMap

 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を双方向に扱っています。

BiMap

Multimap:ValueがコレクションのMap

 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つとなるかと思います。

Multimap

Immutable:不変なコレクション型

 Eclipse Collectionsでは、全てのデータ構造に対して不変なコレクション(Immutable)と可変なコレクション(Mutable)が用意されています。不変なコレクションは追加、削除、更新用のAPIが存在しないため、作成後に変更されることがありません。常に同じ要素を持つことが保証されているため安全に扱うことができ、スレッドセーフでもあります。

ImmutableList<String> oneToFive =
        Lists.immutable.of("One", "Two", "Three", "Four", "Five");
 
oneToFive.add("Six"); // コンパイルエラー!

 コレクションを防御的に扱いたい際にはImmutableコレクションを使ってみましょう。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。