Java 8 Колекционери toMap

1. Въведение

В този бърз урок ще говорим за метода toMap () на класа Collectors . Ще го използваме за събиране на потоци в екземпляр на Map .

За всички примери, разгледани тук, ще използваме списък с книги като отправна точка и ще го трансформираме в различни реализации на Map .

2. Списък на картата

Ще започнем с най-простия случай, като трансформираме Списък в Карта .

Нашият клас „ Книга “ се определя като:

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

И ще създадем списък с книги, за да потвърдим нашия код:

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

За този сценарий ще използваме следното претоварване на метода toMap () :

Collector
    
      toMap(Function keyMapper, Function valueMapper)
    

С toMap можем да посочим стратегии за това как да получим ключа и стойността за картата:

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

И можем лесно да потвърдим, че работи с:

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

3. Разрешаване на ключови конфликти

Примерът по-горе работи добре, но какво ще се случи, ако има дублиран ключ?

Нека си представим, че сме въвели нашата карта до годината на издаване на всяка книга :

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

Като се има предвид по-ранния ни списък с книги, ще видим IllegalStateException :

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

За да го разрешим, трябва да използваме различен метод с допълнителен параметър, mergeFunction :

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

Нека въведем функция за сливане, която показва, че в случай на сблъсък, ние запазваме съществуващия запис:

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

Или, с други думи, получаваме поведение с първа победа:

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

4. Други типове карти

По подразбиране методът toMap () ще върне HashMap .

Но можем ли да върнем различни реализации на Map ? Отговорът е да:

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

Където mapSupplier е функция, която връща нова, празна Карта с резултатите.

4.1. Списък към ConcurrentMap

Да вземем същия пример като по-горе и да добавим функция mapSupplier за връщане на ConcurrentHashMap:

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

Нека продължим и тестваме нашия код:

@Test public void whenCreateConcurrentHashMap() { assertTrue(convertToMap.listToConcurrentMap(bookList) instanceof ConcurrentHashMap); }
4.2. Сортирана карта

И накрая, нека видим как да върнем сортирана карта. За това ще използваме TreeMap като параметър mapSupplier .

Тъй като TreeMap по подразбиране се сортира според естествения ред на ключовете му, не е нужно да сортираме изрично книгите сами:

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

Така че в нашия случай върнатата TreeMap ще бъде сортирана по азбучен ред по името на книгата:

@Test public void whenMapisSorted() { assertTrue(convertToMap.listToSortedMap(bookList).firstKey().equals( "The Fellowship of the Ring")); }
5. Заключение

В тази статия разгледахме метода toMap () на класа Collectors . Позволява ни да създадем нова карта от поток . Също така научихме как да разрешаваме ключови конфликти и да създаваме различни внедрения на карти.

Както винаги кодът е достъпен в GitHub.