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

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

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

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

サマリー

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

11. Bag.collectWithOccurrencesの実装

このメソッドを用いると、Bag データ構造のなかの全てのユニークな要素とその個数を用いてコレクションを変換することができます。引数には要素とその個数を別のオブジェクトに変換する ObjectIntToObjectFunction を指定します。次の例では、要素とその個数を ObjectIntPairインスタンスに変換しています。

@Test
public void collectWithOccurences()
{
    MutableBag<String> source =
            Bags.mutable.with("1", "2", "2", "3", "3", "3");

    MutableBag<ObjectIntPair<String>> targetBag =
            source.collectWithOccurrences(PrimitiveTuples::pair);

    MutableBag<ObjectIntPair<String>> expected =
            Bags.mutable.with(
                    PrimitiveTuples.pair("1", 1),
                    PrimitiveTuples.pair("2", 2),
                    PrimitiveTuples.pair("3", 3));

    Assert.assertEquals(expected, targetBag);
}

12. プリミティブのIterableへのreducereduceIfEmptyの追加

例えば IntList の場合、この reduce メソッドは、longint を受け取り long を返す関数をコレクションの各要素に適用していきます。返り値をワイドニング変換することによって例えば sum のような関数でオーバーフローが起きるのを防ぐことができます。 空のコレクションの場合は NoSuchElementException が投げられるようになっています。

@Test
public void reducePrimitiveIterables()
{
    MutableIntList list =
            IntLists.mutable.with(1, 2, 3, 4, 5);

    long sum = list.reduce(Long::sum);

    Assert.assertEquals(15L, sum);
    Assert.assertEquals(list.sum(), sum);

    Verify.assertThrows(
            NoSuchElementException.class,
            ()-> IntLists.mutable.empty().reduce(Long::sum));
}

空コレクションの場合にも安全に対処したい場合には、 reduceIfEmpty メソッドを使ってデフォルトの返り値を指定することができます。

@Test
public void reduceIfEmptyPrimitiveIterables()
{
    MutableIntList list =
            IntLists.mutable.with(1, 2, 3, 4, 5);

    long sum = list.reduceIfEmpty(Long::sum, 0L);

    Assert.assertEquals(15L, sum);
    Assert.assertEquals(list.sum(), sum);

    Assert.assertEquals(0L,
            IntLists.mutable.empty()
                    .reduceIfEmpty(Long::sum, 0L));
}

13. プリミティブ用の<type1><type2>To<type1>Functionの追加

上の reduce メソッドの例では、IntIterablereduce メソッドは LongIntToLongFunction という新しいインターフェースを引数として受け取っていました。 リリース10.0からは、全てのプリミティブ型の組み合わせに対して2引数/1返り値の関数型インターフェースが用意されています。

@Test
public void newLongTypeToLongFunctions()
{
    MutableIntList intList =
            IntLists.mutable.with(1, 2, 3, 4, 5);

    LongIntToLongFunction sumFunction1 = Long::sum;
    long sum1 = intList.reduceIfEmpty(sumFunction1, 0L);

    Assert.assertEquals(15L, sum1);

    MutableByteList byteList =
            ByteLists.mutable.with((byte) 1, (byte) 2, (byte) 3);

    LongByteToLongFunction sumFunction2 = Long::sum;
    long sum2 = byteList.reduceIfEmpty(sumFunction2, 0L);

    Assert.assertEquals(6L, sum2);
}

14. プリミティブMapへのof/withInitialCapacityメソッドの追加

プリミティブMapのファクトリメソッドで、 ofInitialCapacity または withInitialCapacity を用いて初期サイズを指定することができるようになりました。

@Test
public void ofAndWithInitialCapacity()
{
    MutableIntIntMap map1 =
            IntIntMaps.mutable.ofInitialCapacity(100);
    
    MutableIntIntMap map2 =
            IntIntMaps.mutable.withInitialCapacity(100);
    
    Assert.assertEquals(map1, map2);
}

15. RichIterable.countByEachの実装

新しいメソッド countByEach は、 groupByEachflatCollect と似たものになります。これらは全てなんらかの関数型インターフェースを受け取り Iterable を返します。countByEach の場合の返り値の型は Bag になります。次の例では、3つのクラス(RichIterable.classMutableList.classImmutableList.class)から、全てのメソッドに対して同じ名前を持つメソッドを数えています。ここではオーバーロードやオーバーライドされているメソッドもカウントされます。

@Test
public void countByEach()
{
    MutableList<Class<?>> classes =
            Lists.mutable.with(
                    RichIterable.class,
                    MutableList.class,
                    ImmutableList.class);

    Bag<String> methodNames =
            classes.countByEach(each ->
                    ArrayAdapter.adapt(each.getMethods())
                            .collect(Method::getName));

    Assert.assertEquals(8, 
            methodNames.occurrencesOf("countByEach"));
    Assert.assertEquals(16, 
            methodNames.occurrencesOf("groupByEach"));
    Assert.assertEquals(2, 
            methodNames.occurrencesOf("sortThis"));
}

16. UnifiedSetWithHashingStrategy.addOrReplaceの実装

UnifiedSetWithHashingStrategyaddOrReplace メソッドは、集合内に同じ値が存在した場合に置き換えます。 これは、値を書き換えない add メソッドとは違う挙動をします。

@Test
public void addOrReplace()
{
    UnifiedSetWithHashingStrategy<Integer> set =
            new UnifiedSetWithHashingStrategy<>(
                    HashingStrategies.defaultStrategy());
    Integer one = new Integer(1);
    set.addOrReplace(one);
    Assert.assertSame(one, set.get(1));
    set.add(new Integer(1));
    Assert.assertSame(one, set.get(1));
    Integer otherOne = new Integer(1);
    set.addOrReplace(otherOne);
    Integer integer = set.get(otherOne);
    Assert.assertSame(otherOne, integer);
    Assert.assertNotSame(one, integer);
}

17. UnmodifiableMutableOrderedMapの実装

MutableOrderedMapasUnmodifiable メソッドを呼び出すと、 UnmodifiableMutableOrderedMap インスタンスが返されます。

@Test
public void unmodifiableMutableOrderedMap()
{
    MutableOrderedMap<Object, Object> map =
            OrderedMaps.adapt(new LinkedHashMap<>())
                    .asUnmodifiable();
    
    Verify.assertInstanceOf(
            UnmodifiableMutableOrderedMap.class, 
            map);
}

18. ミュータブルなプリミティブMap上でのwithAllKeyValuesの実装

ミュータブルなプリミティブMapに複数のキーバリューペアをまとめて追加することができるようになりました。

@Test
public void withAllKeyValues()
{
    MutableIntIntMap map =
            IntIntMaps.mutable.empty()
                    .withAllKeyValues(
                            Lists.mutable.with(
                                    PrimitiveTuples.pair(1, 1),
                                    PrimitiveTuples.pair(2, 2),
                                    PrimitiveTuples.pair(3, 3)
                            ))
                    .withAllKeyValues(
                            Lists.mutable.with(
                                    PrimitiveTuples.pair(4, 4),
                                    PrimitiveTuples.pair(5, 5)
                            ));

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

    Assert.assertEquals(expected, map);
}

19. IterableからPrimitivePrimitive/PrimitiveObject/ObjectPrimitiveMapを生成する機能の実装

Iterable に2つの関数型インターフェースを与えることによって、さまざまなプリミティブMapを生成することができるようになりました。1つ目の関数型インターフェースはキーを導出し、2つ目の関数型インターフェースは値を導出するのに使われます。このメソッドは、RichIterabletoMap と非常に似ています。違いは全てのプリミティブMapに対して動作するという点です。

@Test
public void createPrimitiveMapFromIterable()
{
    Iterable<Integer> integers = Interval.oneTo(5);
    MutableIntIntMap map =
            IntIntMaps.mutable.from(
                    integers,
                    key -> key,
                    value -> value);

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

    Assert.assertEquals(expected, map);
}

20. HashingStrategySets.of/withInitialCapacityの実装

HashingStrategySetsofInitialCapacity もしくは withInitialCapacity メソッドを用いて HashingStrategySet を生成することができるようになりました。

@Test
public void hashingStrategySetsOfOrWithInitialCapacity()
{
    MutableSet<Object> set1 =
            HashingStrategySets.mutable.ofInitialCapacity(
                    HashingStrategies.defaultStrategy(),
                    100);

    MutableSet<Object> set2 =
            HashingStrategySets.mutable.withInitialCapacity(
                    HashingStrategies.defaultStrategy(),
                    100);

    Assert.assertEquals(set1, set2);
}

まだまだあります

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

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

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

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