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