1. Въведение
В този урок ще обсъдим няколко примера за използване на Java Stream sза работа с Map s. Струва си да се отбележи, че някои от тези упражнения могат да бъдат решени с помощта на двупосочна структура от данни на картата , но тук се интересуваме от функционален подход.
Първо, ние обясняваме основната идея, която ще използваме за работа с Maps и Stream s. След това представяме няколко различни проблема, свързани с Карти и техните конкретни решения с помощта на Stream s.
2. Основна идея
Основното нещо, което трябва да забележите, е, че потоците са последователности от елементи, които могат лесно да бъдат получени от колекция .
Картите имат различна структура, с картографиране от ключове към стойности, без последователност. Това не означава, че не можем да преобразуваме структурата на Map в различни последователности, които след това ни позволяват да работим по естествен начин с Stream API.
Нека да видим начини за получаване на различни колекции от карта , които след това можем да превърнем в поток :
Map someMap = new HashMap();
Можем да получим набор от двойки ключ-стойност:
Set
entries = someMap.entrySet();
Също така можем да получим набора ключове, свързан с Картата :
Set keySet = someMap.keySet();
Или можем да работим директно с набора от стойности:
Collection values = someMap.values();
Всеки от тях ни дава входна точка за обработка на тези колекции, като получаваме потоци от тях:
Stream
entriesStream = entries.stream(); Stream valuesStream = values.stream(); Stream keysStream = keySet.stream();
3. Получаване на ключове на карта с помощта на Stream s
3.1. Входни данни
Да приемем, че имаме Карта :
Map books = 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 за нея. Можем да използваме Незадължително, за да изразим, че :
Нека приемем за този пример, че се интересуваме от всеки ключ за книга, отговаряща на това заглавие:
Optional optionalIsbn = books.entrySet().stream() .filter(e -> "Effective Java".equals(e.getValue())) .map(Map.Entry::getKey) .findFirst(); assertEquals("978-0134685991", optionalIsbn.get());
Нека анализираме кода. Първо получаваме entrySet от картата , както видяхме по-рано.
Искаме да разгледаме само записите с „Ефективна Java“ като заглавие, така че първата междинна операция ще бъде филтър.
Ние не се интересуваме от целия запис на картата , а от ключа на всеки запис. И така, следващата верижна междинна операция прави точно това: това е операция с карта , която ще генерира нов поток като изход, който ще съдържа само ключовете за записите, които съответстват на заглавието, което търсихме.
Тъй като искаме само един резултат, можем да приложим операцията findFirst () на терминала, която ще предостави началната стойност в потока като незадължителен обект.
Нека видим случай, в който заглавие не съществува:
Optional optionalIsbn = 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“, ще получим повече от един резултат:
List isbnCodes = 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"));
Това, което направихме в този случай, е да заменим условието на филтъра, за да проверим дали стойността в Картата започва с „Ефективна Java“, вместо да сравняваме за равенство на низове .
Този път събираме резултатите - вместо да избираме първия - поставяме мачовете в Списък .
4. Получаване на стойности на картата с помощта на Stream s
Сега, нека се съсредоточим върху различен проблем с картите: Вместо да получаваме ISBN на базата на заглавията , ние ще се опитаме да получим заглавия въз основа на ISBN.
Нека използваме оригиналната карта . Искаме да намерим заглавия, за които ISBN номерът им започва с „978-0“.
List titles = 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"));
Това решение е подобно на решенията на предишния ни набор от проблеми - ние предаваме потока на въведения набор и след това филтрираме, картографираме и събираме.
И както преди, ако искахме да върнем само първото съвпадение, можехме след метода на картата да извикаме метода findFirst () , вместо да събираме всички резултати в Списък .
5. Заключение
Показахме как да обработваме карта по функционален начин .
По-специално, видяхме, че след като преминем към използването на свързаните колекции с Map s, обработката с помощта на Stream s става много по-лесна и интуитивна.
И, разбира се, всички примери могат да бъдат намерени в проекта GitHub.