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); }
И накрая, нека видим как да върнем сортирана карта. За това ще използваме 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")); }
В тази статия разгледахме метода toMap () на класа Collectors . Позволява ни да създадем нова карта от поток . Също така научихме как да разрешаваме ключови конфликти и да създаваме различни внедрения на карти.
Както винаги кодът е достъпен в GitHub.