Eclipse Collections 10.0の新機能 - Part 1 #EclipseCollections

本記事はDonald Raab氏による New Features of Eclipse Collections 10.0 — Part 1 - Donald Raab - Medium を元にした日本語版の記事です。日本語でわかりやすい情報となるように適宜編集しており、日本語訳とは違って必ずしも元の文章を訳したものではありません(本人にrewriteの許可をもらっております)。原文を参考にしたい場合は上記リンクからどうぞ。

Eclipse Collections 10.0の新機能を用いたコード例を紹介していきます。

f:id:itohiro73:20190804213414p:plain
Eclipse Collections 10.0の新機能10個

サマリー

本記事では、Eclipse Collections 10.0がリリースされました #EclipseCollections - itohiro73’s blog で挙げた26個の新機能のうち最初の10個を紹介します。

1. MultiReaderList/Bag/Set に特化したInterface

Eclipse Collectionsには、長らくmulti-reader対応のコレクション実装が存在していましたが、それらに対応するインターフェースは存在していませんでした。10.0以降ではこれらのインターフェースが利用できるようになっています。

f:id:itohiro73:20190804214204p:plain
MultiReaderList/Set/Bagインターフェース

@Test
public void multiReaderList()
{
    MultiReaderList<String> list = 
        Lists.multiReader.with("1", "2", "3");

    list.withWriteLockAndDelegate(backingList -> {
        Iterator<String> iterator = backingList.iterator();
        iterator.next();
        iterator.remove();
    });
    Assert.assertEquals(Lists.mutable.with("2", "3"), list);
}

2. プリミティブListからプリミティブStreamを生成する実装

普通のListからStreamを生成するのは簡単ですが、Eclipse Collections 9.xまではプリミティブListからStreamを生成するのは簡単ではありませんでした。10.0からは下記のようにプリミティブListからプリミティブStreamを簡単に生成できます。

@Test
public void primitiveListToPrimitiveStream()
{
    IntStream intStream1 =
            IntLists.mutable.with(1, 2, 3, 4, 5)
                    .primitiveStream();
    IntStream intStream2 =
            IntLists.immutable.with(1, 2, 3, 4, 5)
                    .primitiveStream();

    LongStream longStream1 =
            LongLists.mutable.with(1L, 2L, 3L, 4L, 5L)
                    .primitiveStream();
    LongStream longStream2 =
            LongLists.immutable.with(1L, 2L, 3L, 4L, 5L)
                    .primitiveStream();

    DoubleStream doubleStream1 =
            DoubleLists.mutable.with(1.0, 2.0, 3.0, 4.0, 5.0)
                    .primitiveStream();
    DoubleStream doubleStream2 =
            DoubleLists.immutable.with(1.0, 2.0, 3.0, 4.0, 5.0)
                    .primitiveStream();
}

3. toMapにおけるターゲットMapのサポート

toMapオーバーロードとして、ターゲットのMapを引数としてとるメソッドが追加されました。

@Test
public void toMapWithTarget()
{
    MutableList<Integer> list =
            Lists.mutable.with(1, 2, 3, 4, 5);

    Map<String, Integer> map =
            list.toMap(String::valueOf,
                       each -> each,
                       new LinkedHashMap<>());

    Map<String, Integer> expected = new LinkedHashMap<>();
    expected.put("1", 1);
    expected.put("2", 2);
    expected.put("3", 3);
    expected.put("4", 4);
    expected.put("5", 5);

    Assert.assertEquals(expected, map);
}

4. MutableMapIterable.removeAllKeysの実装

Mapに対して、あるSetに含まれる要素に対応するキーを削除する removeAllKeys メソッドが利用できます。

@Test
public void removeAllKeys()
{
    MutableMap<Integer, String> map = 
        Maps.mutable.with(1, "1", 2, "2", 3, "3");

    map.removeAllKeys(Sets.mutable.with(1, 3));

    Assert.assertEquals(Maps.mutable.with(2, "2"), map);
}

5 RichIterable.toBiMapの実装

Eclipse Collections 10.0では、RichIterableBiMapに変換することができます。

@Test
public void toBiMap()
{
    MutableBiMap<String, Integer> expected = 
      BiMaps.mutable.with("1", 1, "2", 2, "3", 3);

    MutableBiMap<String, Integer> biMap = 
      Lists.mutable.with(1, 2, 3).toBiMap(String::valueOf, i -> i);

    Assert.assertEquals(expected, biMap);
}

6 Multimap.collectKeyMultiValues の実装

Multimapに対して、keyとvalueの両方に対してfunctionを適用して変換できるcollectKeyMultiValuesメソッドが追加になりました。

@Test
public void collecKeyMultiValues()
{
    MutableListMultimap<String, String> multimap =
            Multimaps.mutable.list.with(
                    "nj", "Monmouth",
                    "nj", "Bergen",
                    "nj", "Union");

    MutableBagMultimap<String, String> transformed =
            multimap.collectKeyMultiValues(
                    String::toUpperCase, 
                    String::toUpperCase);

    Assert.assertEquals(Multimaps.mutable.bag.with(
            "NJ", "MONMOUTH",
            "NJ", "BERGEN",
            "NJ", "UNION"), transformed);
}

7 コレクションファクトリにおけるfromStream(Stream)の実装

ListSetBagStackのそれぞれのファクトリクラスにおいて、Streamからコレクションを生成するfromStreamメソッドが利用できるようになりました。

@Test
public void fromStreamOnCollectionFactories()
{
    MutableList<Integer> 
        list = Lists.mutable.fromStream(Stream.of(1, 2, 3, 4, 5));
    Assert.assertEquals(
        Lists.mutable.with(1, 2, 3, 4, 5), list);

    MutableSet<Integer> set = 
        Sets.mutable.fromStream(Stream.of(1, 2, 3, 4, 5));
    Assert.assertEquals(
        Sets.mutable.with(1, 2, 3, 4, 5), set);

    MutableBag<Integer> bag = 
        Bags.mutable.fromStream(Stream.of(1, 2, 3, 4, 5));
    Assert.assertEquals(
        Bags.mutable.with(1, 2, 3, 4, 5), bag);

    MutableStack<Integer> stack = 
        Stacks.mutable.fromStream(Stream.of(1, 2, 3, 4, 5));
    Assert.assertEquals(
        Stacks.mutable.with(1, 2, 3, 4, 5), stack);
}

8 LazyIterate.cartesianProductの実装

直積集合を求めるcartesianProductメソッドはもともとユーティリティメソッドとして存在していましたが、Setのみをパラメーターとしてとっていたため、他のIterableに対してはは明示的にSetに変換してから呼び出す必要がありました。今回導入された新しい LazyIterate.cartesianProductではどのようなIterableからも直積集合を求めることができます。

@Test
public void cartesianProduct()
{
    MutableList<Integer> numbers = Lists.mutable.with(1, 2, 3);
    MutableList<String> letters = Lists.mutable.with("A", "B", "C");

    MutableList<Pair<String, Integer>> pairs =
            LazyIterate.cartesianProduct(letters, numbers).toList();

    MutableList<Pair<String, Integer>> expected =
            Lists.mutable.with(
                    Tuples.pair("A", 1),
                    Tuples.pair("A", 2),
                    Tuples.pair("A", 3),
                    Tuples.pair("B", 1),
                    Tuples.pair("B", 2),
                    Tuples.pair("B", 3),
                    Tuples.pair("C", 1),
                    Tuples.pair("C", 2),
                    Tuples.pair("C", 3));

    Assert.assertEquals(expected, pairs);
}

9 プリミティブMapへのupdateValuesメソッドの追加

プリミティブMapに対して全てのvalueを更新したい場合、 updateValuesを使うことができます。

@Test
public void updateValues()
{
    MutableIntIntMap map = IntIntMaps.mutable.empty()
            .withKeyValue(1, 5)
            .withKeyValue(2, 3)
            .withKeyValue(3, 10);

    map.updateValues((k, v) -> v + 1);

    MutableIntIntMap expected = IntIntMaps.mutable.empty()
            .withKeyValue(1, 6)
            .withKeyValue(2, 4)
            .withKeyValue(3, 11);

    Assert.assertEquals(expected, map);
}

10 MutableMultimap.getIfAbsentPutAllの実装

MutableMultimap.getIfAbsentPutAllは、MutableMap.getIfAbsentPutと同様の機能を持ちます。違いとしては、MutableMultimap.getIfAbsentPutAllでは複数のvalueをコレクションとしてputすることができる点にあります。

@Test
public void getIfAbsentPutAll()
{
    MutableListMultimap<Integer, Integer> multimap =
            Multimaps.mutable.list.with(2, 1);

    ImmutableList<Integer> defaultValue = 
            Lists.immutable.with(1, 2, 3);
    MutableList<Integer> oneValue = 
            multimap.getIfAbsentPutAll(1, defaultValue);
    MutableList<Integer> twoValue = 
            multimap.getIfAbsentPutAll(2, defaultValue);

    Assert.assertEquals(defaultValue, oneValue);
    Assert.assertEquals(Lists.mutable.with(1), twoValue);
}

まだまだあります

Eclipse Collections 10.0にはまだ16個の機能が残っています。これらも記事として公開していきますのでお楽しみに。

Eclipse Collections 10.0の新機能、楽しんでいただけると幸いです!!

Eclipse Collectionsコントリビューションを受け付けています!どこから始めていいかよくわからないという方はitohiro73までDMいただければ喜んでガイドさせていただきます。

もしライブラリを気に入っていただけて、われわれに知らせたいという方は、Twitterハッシュタグ#EclipseCollectionsをつけてつぶやいていただいたり、もしくはGitHubレポにスターをつけていただけるとめっちゃよろこびますのでどうぞよろしくお願いします!!