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]で見つけることができます。