Javaコレクションフレームワークの比較 (JDK8/GS Collections/Guava)
はじめに
Java Day Tokyo 2015とJJUG CCC 2015 Springで紹介されていたGS Collectionsを使用してみました。実例を用いてJava8のStream APIやGuavaの使用方法と比較します。
環境
問題設定
- 社員のリストから営業部に所属する社員を抽出し、社員番号の昇順で社員名を取得する
- 社員のリストから部署ごとの社員数を求める
※『Javaエンジニア養成読本』特集2の第3章「Stream API を使いこなすために」の例を参考にしました。
社員
public class Employee { private int id; // 社員番号 private String name; // 名前 private Dept dept; // 部署 // 以下、コンストラクタ・getter }
部署
public enum Dept { ACCOUNTS("経理部"), // GENERAL_AFFAIRS("総務部"), // HUMAN_RESOURCES("人事部"), // SALES("営業部"); private String name; // 名前 // 以下、コンストラクタ・getter }
テストデータ
public abstract class EmployeeDomainForTest { protected List<Employee> employeeList; @Before public void setUp(){ this.setUpEmployeeList(); } private void setUpEmployeeList() { this.employeeList = new ArrayList<>(); this.employeeList.add(new Employee(1003, "Bob", Dept.SALES)); this.employeeList.add(new Employee(1007, "Fred", Dept.HUMAN_RESOURCES)); this.employeeList.add(new Employee(1001, "Daniel", Dept.SALES)); this.employeeList.add(new Employee(1010, "Randy", Dept.GENERAL_AFFAIRS)); this.employeeList.add(new Employee(1004, "Matt", Dept.ACCOUNTS)); this.employeeList.add(new Employee(1009, "Tim", Dept.SALES)); this.employeeList.add(new Employee(1006, "Susan", Dept.GENERAL_AFFAIRS)); this.employeeList.add(new Employee(1002, "Julia", Dept.HUMAN_RESOURCES)); this.employeeList.add(new Employee(1005, "Brown", Dept.HUMAN_RESOURCES)); this.employeeList.add(new Employee(1008, "Kevin", Dept.SALES)); } }
実例1
社員のリストから営業部に所属する社員を抽出し、社員番号の昇順で社員名を取得する
JDK8
public class JDK8Test extends EmployeeDomainForTest { @Test public void getEmployeeNamesInSalesSortedById() { List<String> result = this.employeeList.stream() .filter(e -> e.getDept() == Dept.SALES) .sorted(Comparator.comparingInt(Employee::getId)) .map(Employee::getName).collect(Collectors.toList()); assertThat(result, is(Arrays.asList("Daniel", "Bob", "Kevin", "Tim"))); } }
GS Collections
public class GSCollectionsTest extends EmployeeDomainForTest { @Test public void getEmployeeNamesInSalesSortedById() { MutableList<String> result = FastList .newList(this.employeeList) .selectWith(Predicates2.attributeEqual(Employee::getDept), Dept.SALES) .sortThisBy(Employee::getId).collect(Employee::getName); assertThat(result, is(Arrays.asList("Daniel", "Bob", "Kevin", "Tim"))); } }
Guava
public class GuavaTest extends EmployeeDomainForTest { @Test public void getEmployeeNamesInSalesSortedById() { List<Employee> filtered = FluentIterable.from(this.employeeList) .filter(e -> e.getDept() == Dept.SALES) .toSortedList(Comparator.comparingInt(Employee::getId)); List<String> result = FluentIterable.from(filtered) .transform(Employee::getName).toList(); assertThat(result, is(Arrays.asList("Daniel", "Bob", "Kevin", "Tim"))); } }
メソッド対応表
抽出
クラス | メソッド | |
---|---|---|
JDK8 | Stream | filter(Predicate<? super T> predicate) |
GS Collections | MutableList | select(Predicate<? super T> predicate) |
selectWith(Predicate2<? super T,? super P> predicate,P parameter) | ||
Guava | FluentIterable | filter(Predicate<? super E> predicate) |
Iterables | filter(Iterable<T> unfiltered, Predicate<? super T> predicate) |
並び替え
クラス | メソッド | |
---|---|---|
JDK8 | Stream | sorted() |
sorted(Comparator<? super T> comparator) | ||
GS Collections | MutableList | sortThis() |
sortThis(Comparator<? super T> comparator) | ||
sortThisBy(Function<? super T,? extends V> function) | ||
Guava | FluentIterable | toSortedList(Comparator<? super E> comparator) |
変換
クラス | メソッド | |
---|---|---|
JDK8 | Stream | map(Function<? super T,? extends R> mapper) |
GS Collections | MutableList | collect(Function<? super T,? extends V> function) |
collectWith(Function2<? super T,? super P,? extends V> function, P parameter) | ||
Guava | FluentIterable | transform(Function<? super E,T> function) |
Iterables | transform(Iterable<F> fromIterable, Function<? super F,? extends T> function) |
JDK8では、はじめにStreamを生成し、抽出・並び替え・変換の各種操作後、最後に結果を返すという処理の流れになる
GS Collectionsの場合は、抽出・並び替え・変換の各種メソッドはMutableListなどから直接呼べる(つまり、JDK8の場合のようにはじめにStreamを生成する必要はない。ただし、今回の例ではテストデータ
employeeList
が標準のListのため、はじめにMutableListを生成する必要がある)- GS Collectionsには、他にもImmutableListや遅延評価のLazyIterableなどもあり、一部のメソッドを除きMutableListと同様に使用できる
GuavaではFluentIterableまたはIterablesを用いることができるが、FluentIterableはメソッドチェーンでより簡潔に記述できる
実例2
社員のリストから部署ごとの社員数を求める
JDK8
public class JDK8Test extends EmployeeDomainForTest { @Test public void getEmployeeCountByDept() { Map<Dept, Long> result = this.employeeList.stream().collect( Collectors.groupingBy(Employee::getDept, Collectors.counting())); assertThat(result.get(Dept.ACCOUNTS), is(1L)); assertThat(result.get(Dept.GENERAL_AFFAIRS), is(2L)); assertThat(result.get(Dept.HUMAN_RESOURCES), is(3L)); assertThat(result.get(Dept.SALES), is(4L)); } }
GS Collections
public class GSCollectionsTest extends EmployeeDomainForTest { @Test public void getEmployeeCountsByDept() { Bag<Dept> result = FastList.newList(this.employeeList) .collect(Employee::getDept).toBag(); assertThat(result.occurrencesOf(Dept.ACCOUNTS), is(1)); assertThat(result.occurrencesOf(Dept.GENERAL_AFFAIRS), is(2)); assertThat(result.occurrencesOf(Dept.HUMAN_RESOURCES), is(3)); assertThat(result.occurrencesOf(Dept.SALES), is(4)); } }
Guava
public class GuavaTest extends EmployeeDomainForTest { @Test public void getEmployeeCountsByDept() { Iterable<Dept> transformed = Iterables.transform(this.employeeList, Employee::getDept); Multiset<Dept> result = HashMultiset.create(transformed); assertThat(result.count(Dept.ACCOUNTS), is(1)); assertThat(result.count(Dept.GENERAL_AFFAIRS), is(2)); assertThat(result.count(Dept.HUMAN_RESOURCES), is(3)); assertThat(result.count(Dept.SALES), is(4)); } }
メソッド対応表
要素の出現回数
クラス | メソッド | |
---|---|---|
JDK8 | - | - |
GS Collections | Bag | occurrencesOf(Object item) |
Guava | Multiset | count(Object element) |
- GS CollectionsとGuavaでは、それぞれBag, Multiset(要素の重複を許可するSet)を用いることで要素ごとの出現回数を求めることができる
- JDKには上記に相当する型はないが、Collectors#groupingBy(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)の第2引数にCollectors#counting()を渡すことによって、グループごとの出現回数をMapで取得できる
まとめ
実例を用いてJDK8, GS Collections, Guavaの使用方法の一部を確認しました。すべてを併用する状況はあまりないかもしれませんが、それぞれ適切に使い分けられるように違いを把握しておくことが大切だと思います。今回の例では使用しなかったメソッドもまだまだたくさんあるので、さらに理解を深めていきたいです。
- 作者: きしだなおき,のざきひろふみ,吉田真也,菊田洋一,渡辺修司,伊賀敏樹
- 出版社/メーカー: 技術評論社
- 発売日: 2014/12/11
- メディア: Kindle版
- この商品を含むブログ (1件) を見る