😀

EclipseCollections(旧GS Collections)の使い方 - java8

に公開

プロジェクト導入後のメリット・デメリットは下記をご覧ください。
EclipseCollectionsをプロダクトで導入した結果、メリット・デメリット


1, EclipseCollectionsとは

Java のコレクションフレームワークで、かつてGoldman SachsがGS Collectionsとして公開しており
それをEclipseに移管したものです。

2, EclipseCollectionsを使う理由

  1. メモリ効率が良いコンテナ
  2. 豊富かつリッチな関数
  3. 終端操作不要・再利用可能

この辺は他でもよく語られているので割愛しますが、
「こういうことをやりたいなあ」と思って探すとほとんど存在するぐらい、非常に関数群の数が多いです。
またstreamの場合、stream生成・終端操作が必要になりコードがさほど短くならないため導入を見送った経緯がありますが、そういった問題も解決してくれました。

3, どのぐらい短くなる?Example編

下記はシンプルな一例ですが、ユーザーオブジェクトの中から条件を満たす者を抽出する場合です。
これがもっと複雑な処理や、マップなどが絡むと更にコード量が顕著になります。
 また関数の中で渡される変数はイミュータブルであるため、変数の一つ一つのスコープが小さくなることもメリットの1つです。

ユーザオブジェクトの中でスコア50点未満を排除し、男性のみを抽出する。

        List<Integer> userIdList = Arrays.asList();
        for (User user : userList) {
            if(user.getScore() < 50){
                continue;
            }
            if(Sex.MALE == user.getSex()){
                userIdList.add(user.getId());
            }
        }
        return idList;
ユーザオブジェクトの中でスコア50点未満を排除し、男性のみを抽出する。
        userList.reject(user -> user.getScore < 50 )
            .select(user -> Sex.MALE == user.getSex() )
            .collect(adApp -> adApp.getId());

4, EclipseCollections使い方(ImmutableList編)

EclipseCollectionsの中でもよく使うコレクションクラスで、イミュータブルのため変更処理はできません。
変更処理をできるものとしてMutableListが存在します。

4.1 collect

リストに対して操作を加え返却します。

ImmutableList<User> list = Lists.immutable.of(new User(1, "john"), new User(2, "ken"), new User(3, "jim"));
ImmutableList<String> names = list.collect(u -> u.getName());

4.2 select

条件付き取得です。条件に一致するオブジェクトを取得しリストで返却します。

ImmutableList<User> list = Lists.immutable.of(new User(1, "john"), new User(2, "ken"), new User(3, "jim"));
list.collect(u -> u.getId().equals(2));//id=2のみ取得

4.3 detect

条件に一致した最初の要素を取得します。
存在しない場合nullが返却されるので、java8で使用する場合detectIfNoneを利用するか、Optionalでラップする必要があります。

ImmutableList<User> maleList = Lists.immutable.of(new User(1, "john"), new User(2, "ken"), new User(3, "jim"));

User user = maleList.detect(m -> m.getName().equals("john"));
System.out.println(user);
結果
User(id=1, name=john)

4.4 take

リストから指定数を取得します。

ImmutableList<User> maleList = Lists.immutable.of(new User(1, "john"), new User(2, "ken"), new User(3, "jim"));
System.out.println(maleList.take(2));
結果
[User(id=1, name=john), User(id=2, name=ken)]

4.5 drop

リストから指定数の要素を除いたものを返却します。
ただし元のリストに対して変更は行われておりません。

ImmutableList<User> maleList = Lists.immutable.of(new User(1, "john"), new User(2, "ken"), new User(3, "jim"));
System.out.println(maleList.drop(2));
System.out.println(maleList);
結果
[User(id=3, name=jim)]

// 元のmaleListに変更は加わっていません。
[User(id=1, name=john), User(id=2, name=ken), User(id=3, name=jim)]

4.6 subList

添字の範囲を指定してリストから取得します。左引数は条件が以上で、右引数の条件は未満です。

ImmutableList<Integer> numList = Lists.immutable.of(1,2,3,4,5,6,7,8,9,10);
System.out.println(numList.subList(3,6));// 3<=i<6と同様
結果
[4, 5, 6]

4.7 distinct

重複を排除してリストを取得します。

ImmutableList<Integer> numList = Lists.immutable.of(1,2,3,4,4,4,5,6,6);
System.out.println(numList.distinct());
結果
[1, 2, 3, 4, 5, 6]

4.8 reject

リストから指定要素を排除して、リストで取得します。

ImmutableList<Integer> numList = Lists.immutable.of(1,2,3,4,5,6,7,8,9,10);
System.out.println(numList.reject(n -> n >= 4));
結果
[1, 2, 3]

4.9 groupBy

指定したリストから条件を元にマップを生成します。注意点は返却されるのはImmutableMapではなくImmutableListMultimapとなっています。(keyに対してvalueが複数)

key生成条件がユニークな場合は結果も1:1となる。

ImmutableList<User> maleList = Lists.immutable.of(
  new User(1, "john"), new User(2, "ken"), new User(3, "jim"), new User(4, "mike"));

System.out.println(maleList.groupBy(u -> u.getName()));

結果
{jim=[User(id=3, name=jim)], ken=[User(id=2, name=ken)], john=[User(id=1, name=john)], mike=[User(id=4, name=mike)]}

key生成条件がユニークでない場合、結果は1:1ではない。

ImmutableList<User> maleList = Lists.immutable
              .of(new User(1, "john"), new User(2, "ken"), new User(3, "jim"), new User(4, "jim"), new User(5, "john"));
            System.out.println(maleList.groupBy(u -> u.getName()));

結果
{jim=[User(id=3, name=jim), User(id=4, name=jim)], ken=[User(id=2, name=ken)], john=[User(id=1, name=john), User(id=5, name=john)]}

  • 応用

取得したImmutableListMultimapに大して様々な操作ができます。
ここに一例を示します。
 取得したMultimapに対して条件付きで取得する。

ImmutableList<User> maleList = Lists.immutable.of(
new User(1, "john"), new User(2, "ken"), new User(3, "jim"), new User(4, "mike"));

// 取得したkeyに対して条件に一致したものを取得する。第二引数はkeyのサイズが渡される。
System.out.println(
  maleList
    .groupBy(m -> m.getName())
    .selectKeysMultiValues((name, size) -> name.equals("john"))
);
結果
{john=[User(id=1, name=john), User(id=5, name=john)]}

4.10 partition

リストから指定した条件でパーティションを作成します。取得したPartitionImmutableListからはselectedとrejectedを取得できます。

ImmutableList<User> maleList = Lists.immutable.of(
                                  new User(1, "john"),
                                  new User(2, "ken"), 
                                  new User(3, "jim"), 
                                  new User(4, "jim"), 
                                  new User(5, "john")
                                );

PartitionImmutableList<User> userPtList= maleList.partition(m -> m.getId() >=4);
System.out.println("selected : " + userPtList.getSelected());
System.out.println("rejected : " + userPtList.getRejected());
結果
selected : [User(id=4, name=jim), User(id=5, name=john)]
rejected : [User(id=1, name=john), User(id=2, name=ken), User(id=3, name=jim)]

4.11 each

指定リストに対してループ処理を実施します。戻り値はありません。

ImmutableList<User> maleList = Lists.immutable.of(
                                  new User(1, "john"),
                                  new User(2, "ken"), 
                                  new User(3, "jim"), 
                                  new User(4, "jim"), 
                                  new User(5, "john")
                                );
maleList.each( m -> System.out.println(m.getName()));

結果
john
ken
jim
jim
john

4.12 tap

指定リストに対してループ処理を実施します。eachと異なり元のリスト型を返却するので、引き続き処理を実施できます。

ImmutableList<User> maleList = Lists.immutable.of(
                                  new User(1, "john"),
                                  new User(2, "ken"), 
                                  new User(3, "jim"), 
                                  new User(4, "jim"), 
                                  new User(5, "john")
                                );
maleList
  .tap( m -> System.out.println(m.getName()))
  .tap(m ->System.out.println(m.getName()));
結果
john
ken
jim
jim
john
john
ken
jim
jim
john

4.13 zip

指定したリストに対して、同数のリストを渡すことで結合して1:1のPairを返却する。

ImmutableList<Integer> numList = Lists.immutable.of(1, 2, 3, 4);

ImmutableList<Pair<Integer, String>> pairs  = numList.zip(Lists.immutable.of("one","two","three","four"));
System.out.println(pairs);
結果
[1:one, 2:two, 3:three, 4:four]

4.14 asParallel

対象リストに対して並列実行を行う。asParallelの引数はExecutorServiceのため並列処理の種別や使用プロセッサ数も定義できる。

@Cleanup("shutdown") ExecutorService es = Executors.newFixedThreadPool(4);
list
  .asParallel(es, limit) //リミットは実行処理のリミット件数
  .forEach(summary -> {
      // 実行処理
  }

4.15 allSatisfy

対象リストに対して、条件が全て満たされる場合trueを返却します。
1つでも一致しない物があればfalseとなります。

ImmutableList<Integer> numList = Lists.immutable.of(1, 2, 3, 4, 5, 6);
System.out.println(numList.allSatisfy(num -> num >= 3));

結果
false

4.16 anySatisfy

対象リストに対して、条件が1つでも満たされる場合trueとなります。

ImmutableList<Integer> numList = Lists.immutable.of(1, 2, 3, 4, 5, 6);
System.out.println(numList.anySatisfy(num -> num >=6));
結果
true

4.17 noneSatisfy

対象リストに対して、全ての条件が満たされない場合にtrueとなります。

ImmutableList<Integer> numList = Lists.immutable.of(1, 2, 3, 4, 5, 6);
System.out.println(numList.noneSatisfy(num -> num >=10));
結果
true

4.18 maxBy

指定したリストに対して指定した条件での最大値を取得する。

ImmutableList<String> strList = Lists.immutable.of("tom", "john", "david");
System.out.println(strList.maxBy(str -> str.length()));
結果
david

4.19 minBy

maxByの逆です。

ImmutableList<String> strList = Lists.immutable.of("tom", "john", "david");
System.out.println(strList.minBy(str -> str.length()));

結果
tom

5 おまけ

ImmutableList内のOptionalを排除する。

flatCollectを使用して内部のOptionalを排除する。

public static <T> ImmutableList<T> toImt(ImmutableList<Optional<T>> objOptList) {
        return objOptList.flatCollect(objOpt -> 
          objOpt 
            .map(obj -> Lists.immutable.of(obj)) 
            .orElse(Lists.immutable.empty())
        );
    }

6 終わりに

ズラッと羅列しましたが、実はまだまだほんの一部で今回は触れていませんがMap関連やBag, Tupleなども存在しそちらも便利な関数が存在します。また機会があれば紹介できればと思います。

Discussion