Java 8 Collectors toMap

1. Einleitung

In diesem kurzen Tutorial werden wir über die toMap () -Methode der Collectors- Klasse sprechen . Wir werden es verwenden, um Streams in einer Map- Instanz zu sammeln .

Für alle hier behandelten Beispiele verwenden wir eine Liste von Büchern als Ausgangspunkt und wandeln sie in verschiedene Map- Implementierungen um.

2. Liste zur Karte

Wir beginnen mit dem einfachsten Fall, indem wir eine Liste in eine Karte umwandeln .

Unsere Buchklasse ist definiert als:

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

Und wir werden eine Liste von Büchern erstellen, um unseren Code zu validieren:

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

Für dieses Szenario verwenden wir die folgende Überladung der toMap () -Methode:

Collector
    
      toMap(Function keyMapper, Function valueMapper)
    

Mit toMap können wir Strategien angeben, wie der Schlüssel und der Wert für die Karte ermittelt werden können:

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

Und wir können leicht überprüfen, ob es funktioniert mit:

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

3. Schlüsselkonflikte lösen

Das obige Beispiel hat gut funktioniert, aber was würde passieren, wenn es einen doppelten Schlüssel gibt?

Stellen wir uns vor, wir haben unsere Karte bis zum Erscheinungsjahr jedes Buches verschlüsselt :

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

In Anbetracht unserer früheren Liste von Büchern würden wir eine IllegalStateException sehen :

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

Um dies zu beheben, müssen wir eine andere Methode mit einem zusätzlichen Parameter verwenden, der mergeFunction :

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

Lassen Sie uns eine Zusammenführungsfunktion einführen, die angibt, dass wir im Falle einer Kollision den vorhandenen Eintrag beibehalten:

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

Mit anderen Worten, wir erhalten das erste Gewinnverhalten:

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

4. Andere Kartentypen

Standardmäßig gibt eine toMap () -Methode eine HashMap zurück .

Aber können wir verschiedene Map- Implementierungen zurückgeben? Die Antwort ist ja:

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

Wobei der mapSupplier eine Funktion ist, die eine neue, leere Map mit den Ergebnissen zurückgibt .

4.1. Liste zu ConcurrentMap

Nehmen wir das gleiche Beispiel wie oben und fügen eine mapSupplier- Funktion hinzu, um eine ConcurrentHashMap zurückzugeben:

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

Lassen Sie uns fortfahren und unseren Code testen:

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

Zuletzt sehen wir uns an, wie Sie eine sortierte Karte zurückgeben. Dafür verwenden wir eine TreeMap als mapSupplier- Parameter.

Da eine TreeMap standardmäßig nach der natürlichen Reihenfolge ihrer Schlüssel sortiert ist, müssen wir die Bücher nicht explizit selbst sortieren :

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

In unserem Fall wird die zurückgegebene TreeMap in alphabetischer Reihenfolge nach dem Buchnamen sortiert:

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

In diesem Artikel haben wir uns mit der toMap () -Methode der Collectors- Klasse befasst. Es ermöglicht uns, eine neue Karte aus einem Stream zu erstellen . Wir haben auch gelernt, wie man Schlüsselkonflikte löst und verschiedene Kartenimplementierungen erstellt.

Wie immer ist der Code auf GitHub verfügbar.