大家好,又见面了,我是你们的朋友全栈君。
flatMap示例
什么是flatMap()
回顾下面的数据结构,
# Stream<String[]>
# Stream<Stream<String>>
# String[][]
[
[1, 2],
[3, 4],
[5, 6]
]
在Java8中,我们可以使用flatMap将上述数据结构转化为一下结构
# Stream<String>
# String[]
[1, 2, 3, 4, 5, 6]
为什么要平流
处理包含超过一个级别的流,例如Stream<String[]>、Stream<List>或者Stream<Stream>是具有挑战的。将两个级别的流划分为一个级别,例如Stream或者Stream,我们就可以很容易的处理它。
回顾下面这个示例,在使用流上使用flatMap前后对比
下面是一个二维数组,我们使用Arrays.stream或者Stream.of将他转化为流,然后生成String[]或Stream<String[]>的流
String[][] array = new String[][]{
{
"a", "b"}, {
"c", "d"}, {
"e", "f"}};
// array to a stream
Stream<String[]> stream1 = Arrays.stream(array);
// same result
Stream<String[]> stream2 = Stream.of(array);
然后我们得到下面这样的数据结构
[
[a, b],
[c, d],
[e, f]
]
这里我们有一个要求,过滤到a,然后打印所有的字符
首先,我们直接使用Stream#filter,然而下面的程序不会打印任何内容。因为Stream#filter中的x是String[]类型,不是String类型,这条件将总是false,因此Stream流不会收集到任何内容。
String[][] array = new String[][]{
{
"a", "b"}, {
"c", "d"}, {
"e", "f"}};
// convert array to a stream
Stream<String[]> stream1 = Arrays.stream(array);
List<String[]> 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[]类型
String[][] array = new String[][]{
{
"a", "b"}, {
"c", "d"}, {
"e", "f"}};
// array to a stream
Stream<String[]> stream1 = Arrays.stream(array);
// x is a String[]
List<String[]> 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]
在上面例子中,Stream过滤到了整个[a, b],但是我们只想过滤到a
下面是最终版本,我们先组合数组,然后组合过滤器。在Java中,我们转化二维数组为一维数组,我们能够通过遍历二维数组,然后把所有元素放到一个新数组中。或者我们可以使用flatMap将二维数组展平为以为数组,从而将Stream<String[]>转化为Stream。
String[][] array = new String[][]{
{
"a", "b"}, {
"c", "d"}, {
"e", "f"}};
// Java 8
String[] result = Stream.of(array) // Stream<String[]>
.flatMap(Stream::of) // Stream<String>
.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<String> collect = Stream.of(array) // Stream<String[]>
.flatMap(Stream::of) // Stream<String>
.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
需要指出的是,多维数组展平为以为数组是有些复杂的一件事,容易产生误解和出错。我们可以使用Stream#flatMap展平二维流为一维流。
Stream<String[]> -> flatMap -> Stream<String>
Stream<Set<String>> -> flatMap -> Stream<String>
Stream<List<String>> -> flatMap -> Stream<String>
Stream<List<Object>> -> flatMap -> Stream<Object>
flatMap示例,查找所有书
下面示例使用stream()将List转化为对象流,每个对象都是书的集合,我们可以使用flatMap来生成一个流,它包含所有对象的所有数据
在最后,我们可以过滤出包含python的书,然后去掉重复的书籍
Developer.java
import java.util.HashSet;
import java.util.Set;
public class Developer {
private Integer id;
private String name;
private Set<String> book;
//getters, setters, toString
public void addBook(String book) {
if (this.book == null) {
this.book = new HashSet<>();
}
this.book.add(book);
}
}
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<Developer> list = new ArrayList<>();
list.add(o1);
list.add(o2);
// hmm....Set of Set...how to process?
/*Set<Set<String>> collect = list.stream() .map(x -> x.getBook()) .collect(Collectors.toSet());*/
Set<String> collect =
list.stream()
.map(x -> x.getBook()) // Stream<Set<String>>
.flatMap(x -> x.stream()) // Stream<String>
.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<String> collect2 = list.stream()
//.map(x -> x.getBook())
.flatMap(x -> x.getBook().stream()) // Stream<String>
.filter(x -> !x.toLowerCase().contains("python")) // filter python book
.collect(Collectors.toSet());
flatMap示例——order和lineItems
orders是采购订单的一个流,每个采购订单包含订单项的集合,我们可以使用flatMap生成一个Stream流,这个流包含订单中所有的订单项。此外我们使用reduce操作对订单项的金额进行求和。
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
public class FlatMapExample2 {
public static void main(String[] args) {
List<Order> orders = findAll();
/* Stream<List<LineItem>> listStream = orders.stream() .map(order -> order.getLineItems()); Stream<LineItem> lineItemStream = orders.stream() .flatMap(order -> order.getLineItems().stream()); */
// sum the line items' total amount
BigDecimal sumOfLineItems = orders.stream()
.flatMap(order -> order.getLineItems().stream()) // Stream<LineItem>
.map(line -> line.getTotal()) // Stream<BigDecimal>
.reduce(BigDecimal.ZERO, BigDecimal::add); // reduce to sum all
// sum the order's total amount
BigDecimal sumOfOrder = orders.stream()
.map(order -> order.getTotal()) // Stream<BigDecimal>
.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<Order> 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);
}
}
Order.java
public class Order {
private Integer id;
private String invoice;
private List<LineItem> lineItems;
private BigDecimal total;
// getter, setters, constructor
}
LineItem.java
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
flatMap示例——使用空格分割线
读取text文件,使用空格分隔行,展示所有的单词数
F:\\test.txt
hello world Java
hello world Python
hello world Node JS
hello world Rust
hello world Flutter
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<String> lines = Files.lines(path, StandardCharsets.UTF_8);
// stream of array...hard to process.
// Stream<String[]> words = lines.map(line -> line.split(" "));
// stream of stream of string....hmm...better flat to one level.
// Stream<Stream<String>> words = lines.map(line -> Stream.of(line.split(" ")));
// result a stream of words, good!
Stream<String> words = lines.flatMap(line -> Stream.of(line.split(" ")));
// count the number of words.
long noOfWords = words.count();
System.out.println(noOfWords); // 16
}
}
输出
16
flatMap和基本数据类型
对于基本数据类型,像int、long、double等,Java8也提供了相关的flatMapTo{primative type}去展平基本数据类型的流,原理是相同的。
flagMapToInt -> IntStream
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<int[]>
Stream<int[]> streamArray = Stream.of(array);
//Stream<int[]> -> flatMap -> IntStream
IntStream intStream = streamArray.flatMapToInt(x -> Arrays.stream(x));
intStream.forEach(x -> System.out.println(x));
}
}
输出
1
2
3
4
5
6
flatMapToLong -> LongStream
long[] array = {
1, 2, 3, 4, 5, 6};
Stream<long[]> longArray = Stream.of(array);
LongStream longStream = longArray.flatMapToLong(x -> Arrays.stream(x));
System.out.println(longStream.count());
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/137842.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...