本連載では、ゴールドマン・サックス発のオープンソースJavaコレクションフレームワークであるEclipse Collectionsについて、その概要と歴史、機能を中心に紹介します。これまでのJavaやJava 8のStream APIと比較して何が違うのか。Eclipse Collectionsを例に、読者の皆さんがコレクション処理をより深く理解するための一助になればと思います。
ゴールドマン・サックスが開発し、オープンソースソフトウェア(OSS)として公開したJavaコレクションフレームワーク「GS Collections」が、Eclipse Foundationに移行し、「Eclipse Collections」として生まれ変わりました。本連載では、Java 8で導入されたStream APIを改良した使いやすさとパフォーマンスを誇るEclipse Collectionsについて、その概要と歴史、機能、習得するための教材である「Eclipse Collections Kata」の内容を中心に紹介します。
これまでのJavaやJava 8のStream APIと比較して何が違うのか。Eclipse Collectionsを例に、読者の皆さんがコレクション処理をより深く理解するための一助になればと思います。
「Eclipse Collections」は機能が豊富なオープンソースJavaコレクションフレームワークです。JavaのプログラマーであればListやSet、Mapなどのコレクションにはなじみがあると思います。Java 8で導入されたStream APIとラムダ式によって、それまでfor文などで書いていたさまざまな処理をよりスマートに記述できるようになりました。Eclipse Collectionsを使うことでコレクション処理をさらに簡潔に、より良いパフォーマンスで実装できます。Stream APIでは提供されていない豊富な処理も活用することも可能です。
ちなみに、たまに勘違いされてしまうことがあるので念のため書いておきますが、Eclipse Collectionsは汎用的なJavaライブラリですので、Eclipse IDEでなくても使えます。IntelliJ IDEAやNetBeansでも当然使えますし、VimやEmacsでも書けます。
執筆時点の最新リリースバージョンである7.1.0はJava 5以上の環境であれば利用可能です。2016年8月にリリース予定のバージョン8.0.0からはJava 8との親和性を強化した機能が追加される予定で、サポートバージョンはJava 8以上となります。執筆時点でマイルストーンビルドが提供されているので、最新の機能を試したい方は8.0.0-M1を使用してみてください。
百聞は一見に如かずということで、連載第1回ではEclipse Collectionsの概要として下記の各ポイントを簡単な例を用いながら解説していきます(注:本記事において、JDKと記述のある箇所は特に記載がない限りはJDK 8時点での機能を前提としています)。
これらの利点を簡単に説明した後に、最後にEclipse Collectionsの歴史を追っていきます。
Java 8で導入されたラムダ式とStream APIは、Javaにおけるイテレーション処理に大きなパラダイムシフトをもたらしました。詳しくは、連載「Java 8はラムダ式でここまで変わる」をご参照いただければと思いますが、Stream APIとラムダ式やメソッド参照を組み合わせたスマートな処理方法を気に入っている読者の方も多いかと思います。
Eclipse Collectionsを使うと、さらに快適にコレクション処理を記述できます。早速例を見てみましょう。
コレクションに対してある条件を満たすかどうかを判定したり、カウントしたり選択する処理を、Eclipse Collectionsを使用した方法で書いてみます。まずはコレクションを初期化します。
ImmutableList<Integer> integers = Lists.immutable.of(1, 2, 3, 4, 5, 6);
ここでは「ImmutableList」という不変リストを用いて1から6の整数を要素に持つリストを初期化しています。「不変」かどうかはここの例においてあまり重要ではないので、取りあえずImmutableListはいったん初期化したら変更できないリストだと考えておいてもらえればよいでしょう。不変リストについては本記事の後半で説明します。
Eclipse Collectionsを使ったイテレーション処理の例を3つ挙げます。
boolean anySatisfy = integers.anySatisfy(i -> i > 3);
int count = integers.count(i -> i > 3);
ImmutableList<Integer> select = integers.select(i -> i > 3);
いかがでしょう。非常に簡潔に記述できているのが分かるのではないでしょうか。同様の処理をJava 7以前の書き方や、Java 8のStream APIで書いてみて比べてみましょう。
boolean anySatisfy; for(int i: integers){ if(i > 3) { anySatisfy = true; break; } } long count = 0; for(int i: integers){ if(i > 3) { count++; } } List<Integer> select = new ArrayList<>(); for(int i: integers){ if(i > 3) { select.add(i); } }
Java 7以前の書き方だとそもそも記述量が多いですし、処理の内容を読解するのにも少し時間がかかります。
それではStream APIで書くとどうなるでしょうか。
boolean anySatisfy = integers.stream().anyMatch(i -> i > 3); long count = integers.stream().filter(i -> i > 3).count(); List<Integer> select = integers.stream().filter(i -> i > 3).collect(Collectors.toList());
これはEclipse Collectionsの場合と非常に似ていますが、下記の点に注目してみてください。まずStream APIで記述する場合は全ての例でstream()メソッドを余計に呼ぶ必要があります。また、カウントの例であればfilter()、選択の例であればcollect(Collectors.toList())と、冗長な記述が見受けられます。
Stream APIはコレクション操作を便利にするものではありますが、現存するJCF(Javaコレクションフレームワーク)と整合性を保てるように導入されたため、上記のようにどうしても冗長に書かざるを得ないケースがあります。
単純な例なのでそこまで変わらないように感じるかもしれませんが、コレクションのフィルターをするようなコードは日常茶飯事のごとく読み書きするコードなので、メソッド呼び出しが数段減るだけでコーディングの快適度が大きく変わってきます(当然個人差はあると思いますが)。
このように、Eclipse Collectionsはコレクションの処理をJava 7以前の書き方やJava 8のStream APIと比較しても「より簡潔に」記述できます。この簡潔さこそが、コードを書く開発者、読む開発者、双方にとってうれしい第1の大きな利点です。
Copyright © ITmedia, Inc. All Rights Reserved.