Servizi di raccolta Java 8 alla mappa

1. Introduzione

In questo breve tutorial, parleremo del metodo toMap () della classe Collectors . Lo useremo per raccogliere gli stream in un'istanza Map .

Per tutti gli esempi trattati qui, useremo un elenco di libri come punto di partenza e lo trasformeremo in diverse implementazioni di Map .

2. Elenco su mappa

Inizieremo con il caso più semplice, trasformando una lista in una mappa .

La nostra classe Book è definita come:

class Book { private String name; private int releaseYear; private String isbn; // getters and setters }

E creeremo un elenco di libri per convalidare il nostro codice:

List bookList = new ArrayList(); bookList.add(new Book("The Fellowship of the Ring", 1954, "0395489318")); bookList.add(new Book("The Two Towers", 1954, "0345339711")); bookList.add(new Book("The Return of the King", 1955, "0618129111"));

Per questo scenario useremo il seguente overload del metodo toMap () :

Collector
    
      toMap(Function keyMapper, Function valueMapper)
    

Con toMap , possiamo indicare strategie su come ottenere la chiave e il valore per la mappa:

public Map listToMap(List books) { return books.stream().collect(Collectors.toMap(Book::getIsbn, Book::getName)); }

E possiamo facilmente verificare che funzioni con:

@Test public void whenConvertFromListToMap() { assertTrue(convertToMap.listToMap(bookList).size() == 3); }

3. Risoluzione dei conflitti di chiave

L'esempio sopra ha funzionato bene, ma cosa succederebbe se ci fosse una chiave duplicata?

Immaginiamo di aver inserito la nostra mappa in base all'anno di uscita di ogni libro :

public Map listToMapWithDupKeyError(List books) { return books.stream().collect( Collectors.toMap(Book::getReleaseYear, Function.identity())); }

Dato il nostro elenco precedente di libri, vedremmo un'eccezione IllegalStateException :

@Test(expected = IllegalStateException.class) public void whenMapHasDuplicateKey_without_merge_function_then_runtime_exception() { convertToMap.listToMapWithDupKeyError(bookList); }

Per risolverlo, dobbiamo utilizzare un metodo diverso con un parametro aggiuntivo, mergeFunction :

Collector toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction) 

Introduciamo una funzione di unione che indica che, in caso di collisione, manteniamo la voce esistente:

public Map listToMapWithDupKey(List books) { return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(), (existing, replacement) -> existing)); }

O, in altre parole, otteniamo un comportamento per primo vincente:

@Test public void whenMapHasDuplicateKeyThenMergeFunctionHandlesCollision() { Map booksByYear = convertToMap.listToMapWithDupKey(bookList); assertEquals(2, booksByYear.size()); assertEquals("0395489318", booksByYear.get(1954).getIsbn()); }

4. Altri tipi di mappe

Per impostazione predefinita, un metodo toMap () restituirà una HashMap .

Ma possiamo restituire implementazioni di mappe diverse ? La risposta è si:

Collector toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction, Supplier mapSupplier)

Dove mapSupplier è una funzione che restituisce una nuova mappa vuota con i risultati.

4.1. Elenco a ConcurrentMap

Prendiamo lo stesso esempio di cui sopra e aggiungiamo una funzione mapSupplier per restituire un ConcurrentHashMap:

public Map listToConcurrentMap(List books) { return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(), (o1, o2) -> o1, ConcurrentHashMap::new)); }

Andiamo avanti e testiamo il nostro codice:

@Test public void whenCreateConcurrentHashMap() { assertTrue(convertToMap.listToConcurrentMap(bookList) instanceof ConcurrentHashMap); }
4.2. Mappa ordinata

Infine, vediamo come restituire una mappa ordinata. Per questo useremo una TreeMap come parametro mapSupplier .

Poiché una TreeMap è ordinata in base all'ordinamento naturale delle sue chiavi per impostazione predefinita, non dobbiamo ordinare esplicitamente i libri da soli:

public TreeMap listToSortedMap(List books) { return books.stream() .collect( Collectors.toMap(Book::getName, Function.identity(), (o1, o2) -> o1, TreeMap::new)); }

Quindi, nel nostro caso, la TreeMap restituita verrà ordinata in ordine alfabetico in base al nome del libro:

@Test public void whenMapisSorted() { assertTrue(convertToMap.listToSortedMap(bookList).firstKey().equals( "The Fellowship of the Ring")); }
5. conclusione

In questo articolo, abbiamo esaminato il metodo toMap () della classe Collectors . Ci permette di creare una nuova mappa da uno stream . Abbiamo anche imparato a risolvere i conflitti chiave e creare diverse implementazioni di mappe.

Come sempre il codice è disponibile su GitHub.