Java 8 引入了一個(gè)新的抽象概念,叫做流(Stream)。流可以讓你以一種聲明式的方式處理數(shù)據(jù),類似于 SQL 語(yǔ)句。流不僅可以操作集合,還可以操作數(shù)組、文件、生成器等數(shù)據(jù)源。流還支持并行處理,可以充分利用多核 CPU 的性能。
本文將介紹 Java 8 流式編程的基本概念和常用方法,幫助你掌握流式編程的精髓。
什么是流?
流(Stream)是一個(gè)來(lái)自數(shù)據(jù)源的元素序列,并支持聚合操作。流有以下幾個(gè)特點(diǎn):
- 流不存儲(chǔ)元素,而是按需計(jì)算。
- 流的操作不會(huì)改變?cè)磾?shù)據(jù),而是返回一個(gè)新的流。
- 流的操作是延遲執(zhí)行的,只有當(dāng)需要結(jié)果時(shí)才會(huì)執(zhí)行。
- 流支持內(nèi)部迭代,無(wú)需顯示地遍歷元素。
- 流支持串行和并行兩種模式。
如何創(chuàng)建流?
創(chuàng)建流的方式有很多,常見的有以下幾種:
- 從集合或數(shù)組創(chuàng)建流。例如,
List
list = Arrays.asList("a", "b", "c"); Stream stream = list.stream(); - 從 Stream 類的靜態(tài)方法創(chuàng)建流。例如,
Stream
stream = Stream.of("a", "b", "c"); - 從文件或 I/O 通道創(chuàng)建流。例如,
Stream
stream = Files.lines(Paths.get("data.txt")); - 從 Random 或其他生成器創(chuàng)建流。例如,
Stream
stream = Stream.generate(() -> new Random().nextInt(100)); - 從其他類型的流創(chuàng)建流。例如,
IntStream intStream = stream.mapToInt(String::length);
如何操作流?
流提供了很多有用的方法來(lái)對(duì)元素進(jìn)行操作,這些方法可以分為兩類:中間操作(Intermediate Operation)和終端操作(Terminal Operation)。
中間操作會(huì)返回一個(gè)新的流,可以鏈?zhǔn)秸{(diào)用多個(gè)中間操作。中間操作是惰性的,只有當(dāng)遇到終端操作時(shí)才會(huì)執(zhí)行。
終端操作會(huì)產(chǎn)生一個(gè)結(jié)果或副作用,比如計(jì)算平均值、求和、打印輸出等。終端操作會(huì)消耗掉流,執(zhí)行完終端操作后,流就不能再使用了。
下面介紹一些常用的中間操作和終端操作。
中間操作
- filter:根據(jù)條件過(guò)濾元素。例如,
stream.filter(s -> s.length() > 3)
表示只保留長(zhǎng)度大于3的字符串。 - map:對(duì)每個(gè)元素進(jìn)行映射,生成一個(gè)新的元素。例如,
stream.map(String::toUpperCase)
表示把每個(gè)字符串轉(zhuǎn)換成大寫。 - flatMap:對(duì)每個(gè)元素進(jìn)行映射,生成一個(gè)新的流,并把所有的流合并成一個(gè)流。例如,
stream.flatMap(s -> Arrays.stream(s.split("")))
表示把每個(gè)字符串拆分成字符,并合并成一個(gè)字符流。 - distinct:去除重復(fù)的元素。例如,
stream.distinct()
表示去除重復(fù)的字符串。 - sorted:對(duì)元素進(jìn)行排序。例如,
stream.sorted()
表示按照自然順序排序,stream.sorted(Comparator.comparing(String::length))
表示按照長(zhǎng)度排序。 - limit:截取前n個(gè)元素。例如,
stream.limit(10)
表示只保留前10個(gè)元素。 - skip:跳過(guò)前n個(gè)元素。例如,
stream.skip(10)
表示跳過(guò)前10個(gè)元素。
終端操作
- forEach:對(duì)每個(gè)元素執(zhí)行一個(gè)操作。例如,
stream.forEach(System.out::println)
表示打印每個(gè)元素。 - count:返回元素的個(gè)數(shù)。例如,
stream.count()
表示返回流中元素的個(gè)數(shù)。 - collect:將流轉(zhuǎn)換成其他形式。例如,
stream.collect(Collectors.toList())
表示將流轉(zhuǎn)換成列表,stream.collect(Collectors.joining(","))
表示將流中的字符串用逗號(hào)連接起來(lái)。 - reduce:對(duì)流中的元素進(jìn)行歸約操作,生成一個(gè)值。例如,
stream.reduce((s1, s2) -> s1 + s2)
表示將流中的字符串拼接起來(lái),stream.reduce(0, (n1, n2) -> n1 + n2)
表示將流中的整數(shù)求和。 - min:返回最小的元素。例如,
stream.min(Comparator.comparing(String::length))
表示返回長(zhǎng)度最短的字符串。 - max:返回最大的元素。例如,
stream.max(Comparator.comparing(String::length))
表示返回長(zhǎng)度最長(zhǎng)的字符串。 - anyMatch:判斷是否有任意一個(gè)元素滿足條件。例如,
stream.anyMatch(s -> s.startsWith("a"))
表示判斷是否有以a開頭的字符串。 - allMatch:判斷是否所有的元素都滿足條件。例如,
stream.allMatch(s -> s.length() > 3)
表示判斷是否所有的字符串長(zhǎng)度都大于3。 - noneMatch:判斷是否沒(méi)有任何一個(gè)元素滿足條件。例如,
stream.noneMatch(s -> s.contains("z"))
表示判斷是否沒(méi)有包含z的字符串。
如何使用并行流?
并行流是指可以利用多核 CPU 并行處理的流。并行流可以提高性能,但也有一些注意事項(xiàng):
- 并行流不一定比串行流快,因?yàn)椴⑿辛餍枰~外的線程切換和數(shù)據(jù)同步開銷。
- 并行流不一定能保證順序性,因?yàn)槎鄠€(gè)線程同時(shí)處理元素可能會(huì)導(dǎo)致亂序。
- 并行流不適合有狀態(tài)的操作,因?yàn)槎鄠€(gè)線程同時(shí)操作共享狀態(tài)可能會(huì)導(dǎo)致數(shù)據(jù)不一致。
要?jiǎng)?chuàng)建一個(gè)并行流,有以下幾種方式:
- 調(diào)用 parallelStream() 方法。例如,
list.parallelStream()
表示從列表創(chuàng)建一個(gè)并行流。 - 調(diào)用 parallel() 方法。例如,
stream.parallel()
表示把一個(gè)串行流轉(zhuǎn)換成并行流。 - 使用 StreamSupport 類。例如,
StreamSupport.stream(iterable.spliterator(), true)
表示從可迭代對(duì)象創(chuàng)建一個(gè)并行流。
要把一個(gè)并行流轉(zhuǎn)換成串行流,可以調(diào)用 sequential() 方法。例如,stream.sequential()
表示把一個(gè)并行流轉(zhuǎn)換成串行流。
總結(jié)
本文介紹了 Java 8 流式編程的基本概念和常用方法,希望能幫助你理解和使用流式編程,讓你的代碼更優(yōu)雅、更高效、更簡(jiǎn)潔。