Автор оригинала: Rodrigo Graciano.
1. Обзор
В этом кратком руководстве мы поговорим о методе toMap() класса Collectors . Мы будем использовать его для сбора Stream s в экземпляр Map .
Для всех примеров, рассмотренных здесь, мы будем использовать список книг в качестве отправной точки и преобразовывать его в различные Карта реализации.
Дальнейшее чтение:
Руководство по сборщикам Java 8
Соберите поток Java в неизменяемую коллекцию
Новые сборщики потоков в Java 9
2. Список на карте
Мы начнем с самого простого случая, преобразовав Список в Карту .
Вот как мы определяем наш Book класс:
class Book { private String name; private int releaseYear; private String isbn; // getters and setters }
И мы создадим список книг для проверки нашего кода:
ListbookList = 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"));
Для этого сценария мы будем использовать следующую перегрузку метода to Map() :
Collector> toMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper)
С помощью to Map мы можем указать стратегии получения ключа и значения для карты :
public MaplistToMap(List books) { return books.stream().collect(Collectors.toMap(Book::getIsbn, Book::getName)); }
И мы можем легко подтвердить, что это работает:
@Test public void whenConvertFromListToMap() { assertTrue(convertToMap.listToMap(bookList).size() == 3); }
3. Решение Ключевых Конфликтов
Приведенный выше пример работал хорошо, но что произойдет с дубликатом ключа?
Давайте представим, что мы настроили нашу Карту по каждой Книге году выпуска:
public MaplistToMapWithDupKeyError(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); }
Чтобы решить эту проблему, нам нужно использовать другой метод с дополнительным параметром, функцией merge :
CollectortoMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper, BinaryOperator mergeFunction)
Давайте введем функцию слияния, которая указывает, что в случае столкновения мы сохраняем существующую запись:
public MaplistToMapWithDupKey(List books) { return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(), (existing, replacement) -> existing)); }
Или, другими словами, мы получаем поведение первой победы:
@Test public void whenMapHasDuplicateKeyThenMergeFunctionHandlesCollision() { MapbooksByYear = convertToMap.listToMapWithDupKey(bookList); assertEquals(2, booksByYear.size()); assertEquals("0395489318", booksByYear.get(1954).getIsbn()); }
4. Другие Типы Карт
По умолчанию метод toMap() возвращает HashMap .
Но мы можем вернуть разные Map реализации :
CollectortoMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper, BinaryOperator mergeFunction, Supplier mapSupplier)
где поставщик карты – это функция, которая возвращает новую пустую карту с результатами.
4.1. Список в ConcurrentMap
Давайте возьмем тот же пример и добавим функцию map для возврата ConcurrentHashMap :
public MaplistToConcurrentMap(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 в качестве поставщика карт параметра.
Поскольку TreeMap по умолчанию сортируется в соответствии с естественным порядком его ключей, нам не нужно явно сортировать книги самостоятельно:
public TreeMaplistToSortedMap(List books) { return books.stream() .collect( Collectors.toMap(Book::getName, Function.identity(), (o1, o2) -> o1, TreeMap::new)); }
Таким образом, в нашем случае возвращенная Карта дерева будет отсортирована в алфавитном порядке по названию книги:
@Test public void whenMapisSorted() { assertTrue(convertToMap.listToSortedMap(bookList).firstKey().equals( "The Fellowship of the Ring")); }
5. Заключение
В этой статье мы рассмотрели метод toMap() класса Collectors . Это позволяет нам создать новую карту из Потока .
Мы также узнали, как разрешать ключевые конфликты и создавать различные реализации карт.
Как всегда, код доступен на GitHub .