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の新機能を用いたコード例を紹介していきます。
サマリー
本記事では、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へのreduce
とreduceIfEmpty
の追加
例えば IntList
の場合、この reduce
メソッドは、long
と int
を受け取り 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
メソッドの例では、IntIterable
の reduce
メソッドは 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
は、 groupByEach
や flatCollect
と似たものになります。これらは全てなんらかの関数型インターフェースを受け取り Iterable
を返します。countByEach
の場合の返り値の型は Bag
になります。次の例では、3つのクラス(RichIterable.class
、 MutableList.class
、 ImmutableList.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
の実装
UnifiedSetWithHashingStrategy
の addOrReplace
メソッドは、集合内に同じ値が存在した場合に置き換えます。 これは、値を書き換えない 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
の実装
MutableOrderedMap
の asUnmodifiable
メソッドを呼び出すと、 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つ目の関数型インターフェースは値を導出するのに使われます。このメソッドは、RichIterable
の toMap
と非常に似ています。違いは全てのプリミティブ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
の実装
HashingStrategySets
の ofInitialCapacity
もしくは 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レポにスターをつけていただけるとめっちゃよろこびますのでどうぞよろしくお願いします!!