В этой статье объясняется 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
Stream stream1 = 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
Stream stream1 = 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
Stream stream1 = 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"}};
List collect = 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 Set book;
//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)");
List list = 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
карта необязательна.
Set collect2 = 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) {
List orders = 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 List lineItems;
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
Stream lines = 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};
//Stream
Stream 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};
Stream longArray = 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/”