Introduzione a PCollections

1. Panoramica

In questo articolo, esamineremo PCollections, una libreria Java che fornisce raccolte persistenti e immutabili.

Le strutture di dati persistenti (raccolte) non possono essere modificate direttamente durante l'operazione di aggiornamento, ma viene restituito un nuovo oggetto con il risultato dell'operazione di aggiornamento. Non sono solo immutabili ma anche persistenti, il che significa che dopo aver eseguito la modifica, le versioni precedenti della raccolta rimangono invariate.

PCollections è analogo e compatibile con il framework Java Collections.

2. Dipendenze

Aggiungiamo la seguente dipendenza al nostro pom.xml per consentirci di utilizzare PCollections nel nostro progetto:

 org.pcollections pcollections 2.1.2 

Se il nostro progetto è basato su Gradle, possiamo aggiungere lo stesso artefatto al nostro file build.gradle :

compile 'org.pcollections:pcollections:2.1.2'

L'ultima versione può essere trovata su Maven Central.

3. Struttura della mappa ( HashPMap )

HashPMap è una struttura dati mappa persistente. È l'analogo per java.util.HashMap utilizzato per memorizzare dati valore-chiave non nulli.

Possiamo istanziare HashPMap usando metodi statici convenienti in HashTreePMap. Questi metodi statici restituiscono un'istanza HashPMap supportata da un IntTreePMap.

Il metodo static empty () della classe HashTreePMap crea un HashPMap vuoto che non ha elementi, proprio come quando si utilizza il costruttore predefinito di java.util.HashMap :

HashPMap pmap = HashTreePMap.empty();

Esistono altri due metodi statici che possiamo utilizzare per creare HashPMap . Il metodo singleton () crea una HashPMap con una sola voce:

HashPMap pmap1 = HashTreePMap.singleton("key1", "value1"); assertEquals(pmap1.size(), 1);

Il metodo from () crea un HashPMap da un'istanza java.util.HashMap esistente (e altre implementazioni java.util.Map ):

Map map = new HashMap(); map.put("mkey1", "mval1"); map.put("mkey2", "mval2"); HashPMap pmap2 = HashTreePMap.from(map); assertEquals(pmap2.size(), 2);

Sebbene HashPMap erediti alcuni dei metodi da java.util.AbstractMap e java.util.Map , dispone di metodi unici.

Il metodo minus () rimuove una singola voce dalla mappa mentre il metodo minusAll () rimuove più voci. Esistono anche i metodi plus () e plusAll () che aggiungono rispettivamente voci singole e multiple:

HashPMap pmap = HashTreePMap.empty(); HashPMap pmap0 = pmap.plus("key1", "value1"); Map map = new HashMap(); map.put("key2", "val2"); map.put("key3", "val3"); HashPMap pmap1 = pmap0.plusAll(map); HashPMap pmap2 = pmap1.minus("key1"); HashPMap pmap3 = pmap2.minusAll(map.keySet()); assertEquals(pmap0.size(), 1); assertEquals(pmap1.size(), 3); assertFalse(pmap2.containsKey("key1")); assertEquals(pmap3.size(), 0);

È importante notare che la chiamata a put () su pmap genererà un'eccezione UnsupportedOperationException. Poiché gli oggetti PCollections sono persistenti e immutabili, ogni operazione di modifica restituisce una nuova istanza di un oggetto ( HashPMap ).

Passiamo ad esaminare altre strutture dati.

4. Struttura della lista ( TreePVector e ConsPStack )

TreePVector è un analogo persistente di java.util.ArrayList mentre ConsPStack è l'analogo di java.util.LinkedList . TreePVector e ConsPStack hanno metodi statici convenienti per creare nuove istanze, proprio come HashPMap .

Il metodo empty () crea un TreePVector vuoto , mentre il metodo singleton () crea un TreePVector con un solo elemento. C'è anche il metodo from () che può essere utilizzato per creare un'istanza di TreePVector da qualsiasi java.util.Collection .

ConsPStack ha metodi statici con lo stesso nome che raggiungono lo stesso obiettivo.

TreePVector ha metodi per manipolarlo. Ha i metodi meno () e menoAll () per la rimozione degli elementi; il plus () e plusAll () per l'aggiunta di elementi.

Il with () viene utilizzato per sostituire un elemento in un indice specificato e il subList () ottiene un intervallo di elementi dalla raccolta.

Questi metodi sono disponibili anche in ConsPStack .

Consideriamo il seguente frammento di codice che esemplifica i metodi sopra menzionati:

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");

Nello snippet di codice precedente, pSub è un altro oggetto TreePVector ed è indipendente da pV2 . Come si può osservare, pV2 non è stato modificato dall'operazione subList () ; piuttosto un nuovo oggetto TreePVector è stato creato e riempito con elementi di pV2 dall'indice 0 a 2.

Questo è ciò che si intende per immutabilità ed è ciò che accade con tutti i metodi di modifica di PCollections.

5. Imposta struttura ( MapPSet )

MapPSet è un analogo persistente, supportato da mappa, di java.util.HashSet . Può essere convenientemente istanziato dai metodi statici di HashTreePSet - empty () , from () e singleton () . Funzionano nello stesso modo come spiegato negli esempi precedenti.

MapPSet ha i metodi plus () , plusAll () , meno () e minusAll () per la manipolazione dei dati impostati. Inoltre, eredita i metodi da java.util.Set , java.util.AbstractCollection e 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"));

Infine, c'è anche OrderedPSet , che mantiene l'ordine di inserimento degli elementi proprio come java.util.LinkedHashSet .

6. Conclusione

In conclusione, in questo breve tutorial, abbiamo esplorato PCollection, le strutture di dati persistenti che sono analoghe alle raccolte principali che abbiamo a disposizione in Java. Ovviamente, le PCollections Javadoc forniscono una visione più approfondita delle complessità della libreria.

E, come sempre, il codice completo può essere trovato su Github.