
官方文檔
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
What is flatMap()?
# Stream<String[]>
# Stream<Stream<String>>
# String[][]
[
[1, 2],
[3, 4],
[5, 6]
]
它由一個(gè) 2 級(jí) Stream
或一個(gè)二維數(shù)組組成 。
在 Java 8 中,我們可以使用 flatMap
將上述 2 級(jí) Stream
轉(zhuǎn)換為一級(jí) Stream
或?qū)?二維數(shù)組轉(zhuǎn)換為 一維數(shù)組。
# Stream<String>
# String[]
[1, 2, 3, 4, 5, 6]
簡(jiǎn)言之, flatmap
方法讓你把一個(gè)流中的每個(gè)值都換成另一個(gè)流,然后把所有的流連接
起來(lái)成為一個(gè)流。
看一個(gè)簡(jiǎn)單的例子: 使用flatMap
找出單詞列表中各不相同的字符
Why flat a Stream?
處理包含多個(gè)級(jí)別的 Stream ,比如 Stream<String[]>
或 Stream<List<LineItem>>
或 Stream<Stream<String>>
想 將 2 級(jí) Stream 扁平化為一級(jí),如 Stream<String>
或 Stream<LineItem>
,這樣就可以輕松地循環(huán) Stream 并對(duì)其進(jìn)行處理。
來(lái)看個(gè)簡(jiǎn)單的功能實(shí)現(xiàn),以及常犯的一些錯(cuò)誤。
需求: 有 {"a", "b"}, {"c", "d"}, {"e", "f"}
三個(gè)數(shù)組,要求輸出 除去a
之后的數(shù)據(jù)
/**
* filter out the a and print out all the characters
*/
private static void filterAndPrintCharacters() {
String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}};
// convert array to a stream
Stream<String[]> stream = Arrays.stream(array);
// array to a stream [same result]
Stream<String[]> array1 = Stream.of(array);
log.info("==========錯(cuò)誤的方式一===============");
// x is a String[], not String!
List<String[]> result = stream.filter(x -> !x.equals("a"))
.collect(Collectors.toList());
log.info(String.valueOf(result.size()));
result.forEach(x -> log.info(Arrays.toString(x)));
log.info("==========錯(cuò)誤的方式二===============");
List<String[]> result1 = Arrays.stream(array).filter(x -> {
for (String s : x) { // really?
if (s.equals("a")) {
return false;
}
}
return true;
}).collect(Collectors.toList());
log.info(String.valueOf(result1.size()));
result1.forEach(x -> log.info(Arrays.toString(x)));
log.info("============正確的方式 flatMap=============");
log.info("============先測(cè)試轉(zhuǎn)換成一維數(shù)組=============");
// [a, b, c, d, e, f]
String[] objects = Arrays.stream(array)
.flatMap(Stream::of)
.toArray(String[]::new);
Arrays.stream(objects).forEach(x -> log.info("|---->{}", x));
log.info("============開(kāi)始處理=============");
List<String> collect = Arrays.stream(array)
.flatMap(Stream::of)
.filter(x -> !x.equals("a"))
.collect(Collectors.toList());
collect.forEach(x -> log.info(x));
log.info("============處理結(jié)束=============");
}
我們先看看:
[錯(cuò)誤的方式一]
filter(x -> !x.equals("a")) // x 是數(shù)組 ,而非字符串
[錯(cuò)誤的方式二]
x -> {
for (String s : x) { // really?
if (s.equals("a")) {
return false;
}
}
return true;
} // 會(huì)把整個(gè) [a, b] 過(guò)濾出去,而非我們想要過(guò)濾的 a
[正確的方式 ]
// flatMap 將二維數(shù)組轉(zhuǎn)換成意味數(shù)組, 或者可以說(shuō)是從 Stream<String[]> 轉(zhuǎn)換成Stream<String>.
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]
Arrays.stream(objects).forEach(x -> log.info("|---->{}", x));
接下來(lái)我們就可以很輕松地過(guò)濾出來(lái) a了, 就得到了一下最終版本
List<String> collect = Arrays.stream(array)
.flatMap(Stream::of)
.filter(x -> !x.equals("a"))
.collect(Collectors.toList());
collect.forEach(x -> log.info(x));
【小結(jié)】
Stream#flatMap
可以將 2 levels Stream 轉(zhuǎn)換成 1 level Stream.
Stream<String[]> -> flatMap -> Stream<String>
Stream<Set<String>> -> flatMap -> Stream<String>
Stream<List<String>> -> flatMap -> Stream<String>
Stream<List<Object>> -> flatMap -> Stream<Object>
Demo
需求1:Find all books
分析: 使用 stream
將List轉(zhuǎn)換為對(duì)象流,每個(gè)對(duì)象都包含一組書籍,使用flatMap
生成包含所有對(duì)象中所有書籍的流。過(guò)濾掉包含單詞cloud
的書,并收集一個(gè)Set以便于刪除重復(fù)的書。
private static void findAllBooks() {
Developer o1 = new Developer();
o1.setName("artisan");
o1.addBook("Java 8 in Action");
o1.addBook("Spring Boot in Action");
o1.addBook("Effective Java (3nd Edition)");
Developer o2 = new Developer();
o2.setName("小工匠");
o2.addBook("Spring Cloud");
o2.addBook("Effective Java (3nd Edition)");
List<Developer> list = new ArrayList<>();
list.add(o1);
list.add(o2);
// 這....Set of Set...(Set<Set<String>>)咋處理?
Set<Set<String>> collect = list.stream().map(x -> x.getBook()).collect(Collectors.toSet());
// 方式一
Set<String> result = list.stream()
.map(x -> x.getBook())
.flatMap(Collection::stream)
.filter(x -> !x.toLowerCase().contains("cloud"))
.collect(Collectors.toSet());
result.forEach(x -> log.info("element:------>{}", x));
// 方式二
// 當(dāng)然了,map也可以不用,直接在flatMap中 x->x.getBook().stream()
Set<String> result1 = list.stream()
.flatMap(x -> x.getBook().stream())
.filter(x -> !x.toLowerCase().contains("cloud"))
.collect(Collectors.toSet());
result1.forEach(x -> log.info("element:------>{}", x));
}
當(dāng)然了 有個(gè)內(nèi)部類
@Data
static class Developer {
private Integer id;
private String name;
private Set<String> book;
public void addBook(String book) {
if (this.book == null) {
this.book = new HashSet<>();
}
this.book.add(book);
}
}
我們來(lái)來(lái)拆解下 【方式一】的處理過(guò)程如下
總結(jié)下每一步的輸出:
需求2:Order and LineItems
訂單是一個(gè)采購(gòu)訂單流,每個(gè)采購(gòu)訂單都包含一組行項(xiàng)目,然后使用flatMap
生成一個(gè)包含所有訂單中所有行項(xiàng)目的stream
或Stream<LineItem>
。此外,還添加了一個(gè)reduce
操作來(lái)合計(jì)行項(xiàng)目的總金額 .
private static void orderAndLineItems() {
List<Order> orders = findAll();
// sum the order's total amount
// 計(jì)算 order的total 總和
BigDecimal reduce = orders.stream().map(Order::getTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
log.info(reduce.toString());
// sum the line items' total amount
// 計(jì)算 全部的 line的 price 總和
// 方式一 先 map 再flatMap
BigDecimal reduce1 = orders.stream()
.map(Order::getLineItems)
.flatMap(Collection::stream)
.map(line -> line.getTotal())
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 方式二 直接 flatMap
BigDecimal reduce2 = orders.stream()
.flatMap(order -> order.getLineItems().stream())
.map(line -> line.getTotal())
.reduce(BigDecimal.ZERO, BigDecimal::add);
log.info(reduce1.toString());
log.info(reduce2.toString());
}
/**
* 模擬數(shù)據(jù)
*
* @return
*/
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);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Order {
private Integer id;
private String invoice;
private List<LineItem> lineItems;
private BigDecimal total;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class LineItem {
private Integer id;
private String item;
private Integer qty;
private BigDecimal price;
private BigDecimal total;
}
輸出
需求3:Splits the line by spaces
讀取一個(gè)文本文件,計(jì)算單詞數(shù)量
文本文件
hello world Java
hello world Python
hello world Node JS
hello world Rust
hello world Flutter
@SneakyThrows
private static void splitLinesBySpaces() {
Path path = Paths.get("D:\\IdeaProjects\\boot2\\java8review\\src\\main\\java\\com\\artisan\\java8\\stream2\\a.txt");
// 按行讀取
Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8);
// stream of array...hard to process.
// Stream<String[]> stream = 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(" +")));
// +、*、|、\等符號(hào)在正則表達(dá)示中有相應(yīng)的不同意義
// 加號(hào)可用于與字符匹配 1 次或多次。例如,'bre+' 匹配 bre 和 bree,但不匹配 br
// " +" 匹配空格
// result a stream of words, good! 方式一
Stream<String> words = Files.lines(path, StandardCharsets.UTF_8)
.flatMap(line -> Stream.of(line.split(" +")));
System.out.println(words.count());
// 方式二
long count = Files.lines(path, StandardCharsets.UTF_8)
.map(line -> line.split(" +"))
.flatMap(line -> Stream.of(line)).count();
System.out.println(count);
}
需求4: flatMap and primitive type
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-771863.html
private static void flatMap2PrimitiveType() {
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(System.out::println);
// flatMapToLong -> LongStream
long[] array2 = {1, 2, 3, 4, 5, 6};
Stream<long[]> longArray = Stream.of(array2);
LongStream longStream = longArray.flatMapToLong(x -> Arrays.stream(x));
System.out.println(longStream.count());
}
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-771863.html
到了這里,關(guān)于Java8 - Streams flatMap()的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!