PCollectionsの紹介
1概要
この記事では、永続的で不変のコレクションを提供する** Javaライブラリー、https://pcollections.org/[PCollections]を見ていきます。
https://en.wikipedia.org/wiki/Persistent
data
structure[永続データ]構造体(コレクション)は、更新操作中に直接変更することはできません。更新操作の結果を含む新しいオブジェクトが返されることはありません。それらは不変であるだけでなく、永続的でもあります – つまり、変更が行われた後も、以前のバージョンのコレクションは変更されないままです。
PCollectionsは、Java Collectionsフレームワークと類似しており、互換性があります。
2依存関係
プロジェクトでPCollectionsを使用するために、
pom.xml
に次の依存関係を追加しましょう。
<dependency>
<groupId>org.pcollections</groupId>
<artifactId>pcollections</artifactId>
<version>2.1.2</version>
</dependency>
私たちのプロジェクトがGradleベースの場合は、同じ成果物を
build.gradle
ファイルに追加できます。
compile 'org.pcollections:pcollections:2.1.2'
最新版はhttps://search.maven.org/classic/#search%7Cga%7C1%7Ca%3A%22pcollections%22%20g%3A%22org.pcollections%22[Maven Central]で見つけることができます。
3マップ構造(
HashPMap
)
HashPMap
は永続的なマップデータ構造です。これは、null以外のKey-Valueデータの格納に使用される
java.util.HashMap
の類似物です。
HashTreePMapの便利な静的メソッドを使用して
HashPMap__をインスタンス化できます。
HashTreePMap
クラスのstatic
empty()
メソッドは、要素を持たない空の
HashPMap
を作成します –
java.util.HashMap
のデフォルトコンストラクタを使うのと同じです。
HashPMap<String, String> pmap = HashTreePMap.empty();
HashPMap
を作成するために使用できる他の2つの静的メソッドがあります。
singleton()
メソッドは、エントリを1つだけ持つ
HashPMap
を作成します。
HashPMap<String, String> pmap1 = HashTreePMap.singleton("key1", "value1");
assertEquals(pmap1.size(), 1);
from()
メソッドは、既存の
java.util.HashMap
インスタンス(およびその他の
java.util.Map
実装)から
HashPMap
を作成します。
Map map = new HashMap();
map.put("mkey1", "mval1");
map.put("mkey2", "mval2");
HashPMap<String, String> pmap2 = HashTreePMap.from(map);
assertEquals(pmap2.size(), 2);
HashPMap
は
java.util.AbstractMap
および
java.util.Map
のメソッドの一部を継承していますが、それに固有のメソッドがあります。
minus()
メソッドはマップから単一のエントリを削除し、
minusAll()
メソッドは複数のエントリを削除します。それぞれ単一のエントリと複数のエントリを追加する
plus()
メソッドと
plusAll()
メソッドもあります。
HashPMap<String, String> pmap = HashTreePMap.empty();
HashPMap<String, String> pmap0 = pmap.plus("key1", "value1");
Map map = new HashMap();
map.put("key2", "val2");
map.put("key3", "val3");
HashPMap<String, String> pmap1 = pmap0.plusAll(map);
HashPMap<String, String> pmap2 = pmap1.minus("key1");
HashPMap<String, String> pmap3 = pmap2.minusAll(map.keySet());
assertEquals(pmap0.size(), 1);
assertEquals(pmap1.size(), 3);
assertFalse(pmap2.containsKey("key1"));
assertEquals(pmap3.size(), 0);
pmap
で
put()を呼び出すと、
UnsupportedOperationExceptionがスローされることに注意してください。PCollectionsオブジェクトは永続的で不変なので、すべての変更操作はオブジェクトの新しいインスタンス(
HashPMap
)を返します。
他のデータ構造を見てみましょう。
4リスト構造(
TreePVectorとConsPStack
)
TreePVector
は
java.util.ArrayList
の永続的な類似物であり、
ConsPStack
は
java.util.LinkedList
の類似物です。
TreePVector
と
ConsPStack
には、
HashPMap
と同じように、新しいインスタンスを作成するための便利な静的メソッドがあります。
empty()
メソッドは空の
TreePVector
を作成し、
singleton()
メソッドは1つの要素のみを持つ
TreePVector
を作成します。
任意の
java.util.Collection
から
TreePVector
のインスタンスを作成するために使用できる
from()
メソッドもあります。
ConsPStack
には、同じ目的を達成するための同じ名前の静的メソッドがあります。
TreePVector
にはそれを操作するためのメソッドがあります。要素を削除するための
minus()
メソッドと
minusAll()
メソッドがあります。
plus()
、および
plusAll()
は要素を追加するためのものです。
with()
は指定されたインデックスの要素を置き換えるために使用され、
subList()
はコレクションから要素の範囲を取得します。
これらのメソッドは
ConsPStack
でも利用できます。
上記のメソッドを例示する次のコードスニペットを考えてみましょう。
TreePVector pVector = TreePVector.empty();
TreePVector pV1 = pVector.plus("e1");
TreePVector pV2 = pV1.plusAll(Arrays.asList("e2", "e3", "e4"));
assertEquals(1, pV1.size());
assertEquals(4, pV2.size());
TreePVector pV3 = pV2.minus("e1");
TreePVector pV4 = pV3.minusAll(Arrays.asList("e2", "e3", "e4"));
assertEquals(pV3.size(), 3);
assertEquals(pV4.size(), 0);
TreePVector pSub = pV2.subList(0, 2);
assertTrue(pSub.contains("e1") && pSub.contains("e2"));
TreePVector pVW = (TreePVector) pV2.with(0, "e10");
assertEquals(pVW.get(0), "e10");
上記のコードスニペットでは、
pSub
は別の
TreePVector
オブジェクトであり、
pV2
とは無関係です。観察できるように、
pV2
は
subList()
操作によって変更されませんでした。むしろ、新しい
TreePVector
オブジェクトが作成され、インデックス0から2の
pV2
の要素で埋められました。
これは不変性によって意味されるものであり、それはPCollectionsのすべての修正方法で起こるものです。
5構造体の設定(
MapPSet
)
MapPSet
は、
java.util.HashSet
の、マップに基づいた永続的な類似物です。それは
HashTreePSet –
empty()
、
from()
および
singleton()
の静的メソッドによって都合よくインスタンス化することができます。これらは、前の例で説明したのと同じように機能します。
MapPSet
には、設定データを操作するための
plus()
、
plusAll()
、
minus()
、および
minusAll()
メソッドがあります。さらに、
java.util.Set
、
java.util.AbstractCollection
、および
java.util.AbstractSet
のメソッドを継承しています。
MapPSet pSet = HashTreePSet.empty()
.plusAll(Arrays.asList("e1","e2","e3","e4"));
assertEquals(pSet.size(), 4);
MapPSet pSet1 = pSet.minus("e4");
assertFalse(pSet1.contains("e4"));
最後に、
OrderedPSet
もあります。これは
java.util.LinkedHashSet
のように要素の挿入順序を維持します。
6. 結論
結論として、このクイックチュートリアルでは、PCollections、つまりJavaで利用可能なコアコレクションと類似した永続的なデータ構造について調べました。もちろん、PCollections
Javadoc
は、ライブラリの複雑さについてのさらなる洞察を提供します。
そしていつものように、完全なコードはhttps://github.com/eugenp/tutorials/tree/master/libraries[over Github]で見つけることができます。