В этой статье объясняется Java 8 Stream.flatMap и как его использовать.
Тема
- Что такое flatMap?
- Зачем выравнивать ручей?
- Пример плоской карты – Найдите набор книг.
- Пример плоской карты – Порядок и линейные элементы.
- Пример плоской карты – Разбивает строку на пробелы.
- Плоская карта и примитивный тип
1. Что такое flatMap()?
1.1 Ознакомьтесь с приведенной ниже структурой. Он состоит из 2-уровневого потока или 2d-массивов.
# Stream# Stream > # String[][] [ [1, 2], [3, 4], [5, 6] ]
В Java 8 мы можем использовать flatMap
для преобразования потока вышеуказанных 2 уровней в один уровень потока или 2d-массива в 1d-массив.
# Stream# String[] [1, 2, 3, 4, 5, 6]
2. Зачем выравнивать ручей?
2.1 Сложно обрабатывать поток, содержащий более одного уровня, например Поток<Строка[]>
или Поток<Список<Элемент строки>>
или Поток<Поток<Строка>>
. И мы сглаживаем поток 2 уровней в один уровень, например Stream<Строка>
или Поток <Элемент строки>
, чтобы мы могли легко зациклить поток и обработать его.
Просмотрите приведенный ниже пример до и после применения flatMap
к потоку.
2.2 Ниже приведен 2d-массив, и мы можем использовать Arrays.stream
или Stream.of
для преобразования его в поток, и он создает поток String[]
или Stream<Строка[]>
.
String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}}; // array to a stream Streamstream1 = Arrays.stream(array); // same result Stream stream2 = Stream.of(array);
или вот так.
[ [a, b], [c, d], [e, f] ]
2.3 Вот требование, мы хотим отфильтровать a
и распечатать все символы.
Сначала мы попробуем Поток#фильтр
напрямую. Однако приведенная ниже программа ничего не напечатает, и это потому, что x
внутри фильтра Stream#
является Строкой[]
, а не строкой
; условие всегда будет оставаться ложным, и поток ничего не соберет.
String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}}; // convert array to a stream Streamstream1 = Arrays.stream(array); List result = stream1 .filter(x -> !x.equals("a")) // x is a String[], not String! .collect(Collectors.toList()); System.out.println(result.size()); // 0 result.forEach(System.out::println); // print nothing?
Хорошо, на этот раз мы изменим метод фильтра, чтобы иметь дело со строкой []
.
String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}}; // array to a stream Streamstream1 = Arrays.stream(array); // x is a String[] List result = stream1 .filter(x -> { for(String s : x){ // really? if(s.equals("a")){ return false; } } return true; }).collect(Collectors.toList()); // print array result.forEach(x -> System.out.println(Arrays.toString(x)));
Выход
[c, d] [e, f]
В приведенном выше случае Поток#фильтр
отфильтрует весь [a, b]
, но мы хотим отфильтровать только символ a
3.4 Ниже приведена окончательная версия, и мы сначала объединяем массив, а затем применяем фильтр. В Java для преобразования 2d-массива в 1d-массив мы можем зациклить 2d-массив и поместить все элементы в новый массив; Или мы можем использовать Java 8 flatMap
для выравнивания 2d-массива в 1d-массив или из Потока<Строка[]>
в Поток<Строка>
.
String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}}; // Java 8 String[] result = Stream.of(array) // Stream.flatMap(Stream::of) // Stream .toArray(String[]::new); // [a, b, c, d, e, f] for (String s : result) { System.out.println(s); }
Выход
a b c d e f
Теперь мы можем легко отфильтровать a
; давайте посмотрим окончательную версию.
String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}}; Listcollect = Stream.of(array) // Stream .flatMap(Stream::of) // Stream .filter(x -> !"a".equals(x)) // filter out the a .collect(Collectors.toList()); // return a List collect.forEach(System.out::println);
Выход
b c d e f
Я хочу отметить, что работа с более чем одним уровнем потока является сложной, запутанной и подверженной ошибкам, и мы можем использовать этот Поток#flatMap
, чтобы сгладить поток 2 уровней в поток одного уровня.
Stream-> flatMap -> Stream Stream > -> flatMap -> Stream Stream > -> flatMap -> Stream
Stream > -> flatMap -> Stream
3. Пример плоской карты – Найти все книги.
В этом примере используется .stream()
для преобразования Списка
в поток объектов, и каждый объект содержит набор книг, и мы можем использовать flatMap
для создания потока, содержащего все книги во всех объектах.
В конце концов, мы также отфильтровываем книгу, содержащую слово python
, и собираем Набор
, чтобы удалить дублированную книгу.
package com.mkyong.java8.stream.flatmap; import java.util.HashSet; import java.util.Set; public class Developer { private Integer id; private String name; private Setbook; //getters, setters, toString public void addBook(String book) { if (this.book == null) { this.book = new HashSet<>(); } this.book.add(book); } }
package com.mkyong.java8.stream.flatmap; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class FlatMapExample1 { public static void main(String[] args) { Developer o1 = new Developer(); o1.setName("mkyong"); o1.addBook("Java 8 in Action"); o1.addBook("Spring Boot in Action"); o1.addBook("Effective Java (3nd Edition)"); Developer o2 = new Developer(); o2.setName("zilap"); o2.addBook("Learning Python, 5th Edition"); o2.addBook("Effective Java (3nd Edition)"); Listlist = new ArrayList<>(); list.add(o1); list.add(o2); // hmm....Set of Set...how to process? /*Set > collect = list.stream() .map(x -> x.getBook()) .collect(Collectors.toSet());*/ Set collect = list.stream() .map(x -> x.getBook()) // Stream > .flatMap(x -> x.stream()) // Stream .filter(x -> !x.toLowerCase().contains("python")) // filter python book .collect(Collectors.toSet()); // remove duplicated collect.forEach(System.out::println); } }
Выход
Spring Boot in Action Effective Java (3nd Edition) Java 8 in Action
карта
необязательна.
Setcollect2 = list.stream() //.map(x -> x.getBook()) .flatMap(x -> x.getBook().stream()) // Stream .filter(x -> !x.toLowerCase().contains("python")) // filter python book .collect(Collectors.toSet());
4. Пример плоской карты – Порядок и линейные элементы
Этот пример похож на официальный flatMap JavaDoc пример.
заказы
– это поток заказов на покупку, и каждый заказ на покупку содержит набор позиций, затем мы можем использовать flatMap
для создания потока или Потока <Позиция>
, содержащего все позиции во всех заказах. Кроме того, мы также добавляем операцию уменьшить
для суммирования общей суммы позиций.
package com.mkyong.java8.stream.flatmap; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; public class FlatMapExample2 { public static void main(String[] args) { Listorders = findAll(); /* Stream > listStream = orders.stream() .map(order -> order.getLineItems()); Stream
lineItemStream = orders.stream() .flatMap(order -> order.getLineItems().stream()); */ // sum the line items' total amount BigDecimal sumOfLineItems = orders.stream() .flatMap(order -> order.getLineItems().stream()) // Stream .map(line -> line.getTotal()) // Stream .reduce(BigDecimal.ZERO, BigDecimal::add); // reduce to sum all // sum the order's total amount BigDecimal sumOfOrder = orders.stream() .map(order -> order.getTotal()) // Stream .reduce(BigDecimal.ZERO, BigDecimal::add); // reduce to sum all System.out.println(sumOfLineItems); // 3194.20 System.out.println(sumOfOrder); // 3194.20 if (!sumOfOrder.equals(sumOfLineItems)) { System.out.println("The sumOfOrder is not equals to sumOfLineItems!"); } } // create dummy records private static List findAll() { LineItem item1 = new LineItem(1, "apple", 1, new BigDecimal("1.20"), new BigDecimal("1.20")); LineItem item2 = new LineItem(2, "orange", 2, new BigDecimal(".50"), new BigDecimal("1.00")); Order order1 = new Order(1, "A0000001", Arrays.asList(item1, item2), new BigDecimal("2.20")); LineItem item3 = new LineItem(3, "monitor BenQ", 5, new BigDecimal("99.00"), new BigDecimal("495.00")); LineItem item4 = new LineItem(4, "monitor LG", 10, new BigDecimal("120.00"), new BigDecimal("1200.00")); Order order2 = new Order(2, "A0000002", Arrays.asList(item3, item4), new BigDecimal("1695.00")); LineItem item5 = new LineItem(5, "One Plus 8T", 3, new BigDecimal("499.00"), new BigDecimal("1497.00")); Order order3 = new Order(3, "A0000003", Arrays.asList(item5), new BigDecimal("1497.00")); return Arrays.asList(order1, order2, order3); } }
public class Order { private Integer id; private String invoice; private ListlineItems; private BigDecimal total; // getter, setters, constructor }
public class LineItem { private Integer id; private String item; private Integer qty; private BigDecimal price; private BigDecimal total; // getter, setters, constructor }
Выход
3194.20 3194.20
5. Пример плоской карты – Разбивает строку на пробелы
В этом примере считывается текстовый файл, строка разделяется пробелами и отображается общее количество слов.
Текстовый файл.
hello world Java hello world Python hello world Node JS hello world Rust hello world Flutter
Прочитайте комментарий для пояснения.
package com.mkyong.java8.stream.flatmap; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.stream.Stream; public class FlatMapExample3 { public static void main(String[] args) throws IOException { Path path = Paths.get("C:\\test\\test.txt"); // read file into a stream of lines Streamlines = Files.lines(path, StandardCharsets.UTF_8); // stream of array...hard to process. // Stream words = lines.map(line -> line.split(" +")); // stream of stream of string....hmm...better flat to one level. // Stream > words = lines.map(line -> Stream.of(line.split(" +"))); // result a stream of words, good! Stream words = lines.flatMap(line -> Stream.of(line.split(" +"))); // count the number of words. long noOfWords = words.count(); System.out.println(noOfWords); // 16 } }
Выход
16
6. Плоская карта и примитивный тип
Для примитивных типов, таких как int
, long
, double
и т.д. Поток Java 8 также предоставляет связанный flatMapTo{примитивный тип}
для выравнивания потока примитивного типа; концепция та же.
Плоская карта
-> Внутренний поток
package com.mkyong.java8.stream.flatmap; import java.util.Arrays; import java.util.stream.IntStream; import java.util.stream.Stream; public class FlatMapExample4 { public static void main(String[] args) { int[] array = {1, 2, 3, 4, 5, 6}; //StreamStream streamArray = Stream.of(array); //Stream -> flatMap -> IntStream IntStream intStream = streamArray.flatMapToInt(x -> Arrays.stream(x)); intStream.forEach(x -> System.out.println(x)); } }
Выход
1 2 3 4 5 6
flatMapТоЛонг
-> Длинный поток
long[] array = {1, 2, 3, 4, 5, 6}; StreamlongArray = Stream.of(array); LongStream longStream = longArray.flatMapToLong(x -> Arrays.stream(x)); System.out.println(longStream.count());
Скачать Исходный Код
$клон git $клон git
$компакт-диск java8
Рекомендации
- Поток.flatMap JavaDoc
- Разница между map() flatMap()
- Java – Как объединять массивы
- Java 8 – Как распечатать массив
- Java 8 – Пример группировки коллекторов и сопоставления
- Поток Java 8.уменьшить() примеры
Оригинал: “https://mkyong.com/java8/java-8-flatmap-example/”