1. введение
В этом уроке мы обсудим некоторые примеры использования потоков Java | для работы с Map s . Стоит отметить, что некоторые из этих упражнений могут быть решены с использованием двунаправленной структуры данных Map , но здесь нас интересует функциональный подход.
Во-первых, мы объясним основную идею, которую мы будем использовать для работы с Maps и Stream s. Затем мы представим несколько различных проблем, связанных с Maps и их конкретными решениями с использованием Stream s.
Дальнейшее чтение:
Объединение двух карт с Java 8
Java 8 Коллекторы toMap
Учебник по потоковому API Java 8
2. Основная Идея
Главное, что следует отметить, это то, что Stream s-это последовательности элементов, которые можно легко получить из Коллекции .
Карты имеют другую структуру, с отображением от ключей к значениям, без последовательности. Однако это не означает, что мы не можем преобразовать структуру Map в различные последовательности, которые затем позволят нам естественным образом работать с API потока.
Давайте рассмотрим способы получения различных Коллекций из Карты , которые затем мы можем превратить в Поток :
MapsomeMap = new HashMap<>();
Мы можем получить набор пар ключ-значение:
Set> entries = someMap.entrySet();
Мы также можем получить набор ключей, связанный с картой :
SetkeySet = someMap.keySet();
Или мы могли бы работать непосредственно с набором значений:
Collectionvalues = someMap.values();
Каждый из них дает нам точку входа для обработки этих коллекций путем получения потоков из них:
Stream> entriesStream = entries.stream(); Stream valuesStream = values.stream(); Stream keysStream = keySet.stream();
3. Получение ключей Карты С помощью Потоков
3.1. Входные данные
Предположим, у нас есть Карта :
Mapbooks = new HashMap<>(); books.put( "978-0201633610", "Design patterns : elements of reusable object-oriented software"); books.put( "978-1617291999", "Java 8 in Action: Lambdas, Streams, and functional-style programming"); books.put("978-0134685991", "Effective Java");
Мы заинтересованы в том, чтобы найти ISBN для книги под названием “Эффективная Java.”
3.2. Получение совпадения
Поскольку название книги не может существовать в нашей карте , мы хотим иметь возможность указать, что для нее нет связанного ISBN. Мы можем использовать Необязательно , чтобы выразить это:
Давайте предположим для этого примера, что нас интересует любой ключ для книги, соответствующей этому названию:
OptionaloptionalIsbn = books.entrySet().stream() .filter(e -> "Effective Java".equals(e.getValue())) .map(Map.Entry::getKey) .findFirst(); assertEquals("978-0134685991", optionalIsbn.get());
Давайте проанализируем код. Во-первых, мы получаем entrySet из Map , как мы видели ранее.
Мы хотим рассматривать только записи с “Эффективной Java” в качестве заголовка, поэтому первой промежуточной операцией будет фильтр .
Нас интересует не вся запись Map , а ключ каждой записи. Таким образом, следующая цепная промежуточная операция делает именно это: это операция map , которая создаст новый поток в качестве вывода, который будет содержать только ключи для записей, соответствующих названию, которое мы искали.
Поскольку нам нужен только один результат, мы можем применить операцию findFirst() terminal, которая предоставит начальное значение в потоке в качестве Необязательного объекта.
Давайте рассмотрим случай, в котором название не существует:
OptionaloptionalIsbn = books.entrySet().stream() .filter(e -> "Non Existent Title".equals(e.getValue())) .map(Map.Entry::getKey).findFirst(); assertEquals(false, optionalIsbn.isPresent());
3.3. Получение Нескольких Результатов
Теперь давайте изменим проблему, чтобы увидеть, как мы могли бы справиться с возвращением нескольких результатов вместо одного.
Чтобы получить несколько результатов, давайте добавим следующую книгу в нашу Карту :
books.put("978-0321356680", "Effective Java: Second Edition");
Так что теперь, если мы будем искать все книги, которые начинаются с “Эффективной Java”, мы получим более одного результата:
ListisbnCodes = books.entrySet().stream() .filter(e -> e.getValue().startsWith("Effective Java")) .map(Map.Entry::getKey) .collect(Collectors.toList()); assertTrue(isbnCodes.contains("978-0321356680")); assertTrue(isbnCodes.contains("978-0134685991"));
Что мы сделали в этом случае, так это заменили условие фильтра, чтобы проверить, начинается ли значение в Map с “Эффективной Java” вместо сравнения для String равенства.
На этот раз мы собираем результаты , вместо того, чтобы просто выбирать первый, и помещаем совпадения в Список .
4. Получение значений Карты С помощью потоков
Теперь давайте сосредоточимся на другой проблеме с картами. Вместо получения ISBNs на основе titles , мы попытаемся получить titles на основе ISBNS.
Давайте использовать исходную карту . Мы хотим найти названия с ISBN, начинающимся с “978-0”.
Listtitles = books.entrySet().stream() .filter(e -> e.getKey().startsWith("978-0")) .map(Map.Entry::getValue) .collect(Collectors.toList()); assertEquals(2, titles.size()); assertTrue(titles.contains( "Design patterns : elements of reusable object-oriented software")); assertTrue(titles.contains("Effective Java"));
Это решение аналогично решениям нашего предыдущего набора проблем; мы передаем набор записей в потоковом режиме, а затем фильтруем, сопоставляем и собираем.
Также, как и раньше, если бы мы хотели вернуть только первое совпадение, то после метода map мы могли бы вызвать метод findFirst() вместо сбора всех результатов в List .
5. Заключение
В этой статье мы продемонстрировали, как обрабатывать Карту функциональным способом .
В частности, мы видели, что как только мы переключаемся на использование связанных коллекций в Map s, обработка с использованием Stream s становится намного проще и интуитивно понятной.
Конечно, все примеры в этой статье можно найти в проекте GitHub .