1、Stream簡(jiǎn)介
Stream作為Java 8的一大亮點(diǎn)留量,是對(duì)集合(Collection)、數(shù)組對(duì)象功能的增強(qiáng)哟冬,它專注于對(duì)集合對(duì)象進(jìn)行各種非常便利楼熄、高效的聚合操作,或者大批量數(shù)據(jù)操作浩峡。Stream API借助于同樣新出現(xiàn)的Lambda表達(dá)式孝赫,極大的提高編程效率和程序可讀性。同時(shí)它提供串行和并行兩種模式進(jìn)行匯聚操作红符,并發(fā)模式能夠充分利用多核處理器的優(yōu)勢(shì)青柄,使用fork/join并行方式來(lái)拆分任務(wù)和加速處理過(guò)程伐债。通常編寫并行代碼很難且容易出錯(cuò),但使用Stream API無(wú)需編寫一行多線程的代碼致开,就可以方便的寫出高性能的并發(fā)程序峰锁。所以說(shuō),Java 8中首次出現(xiàn)的java.util.stream是一個(gè)函數(shù)式語(yǔ)言+多核時(shí)代綜合影響的產(chǎn)物双戳。
Stream是一組用來(lái)處理數(shù)組虹蒋、集合的API
Java 8之所以費(fèi)這么大功夫引入函數(shù)式編程,原因有二:
- 代碼簡(jiǎn)潔:函數(shù)式編程寫出的代碼簡(jiǎn)潔且意圖明確飒货,使用Stream接口讓你從此告別for循環(huán)魄衅。
- 多核友好:Java函數(shù)式編程使得編寫并行程序從未如此簡(jiǎn)單,你需要的全部就是調(diào)用一個(gè)parallel()方法塘辅。
2晃虫、Stream特性
- 不是數(shù)據(jù)結(jié)構(gòu),沒(méi)有內(nèi)部存儲(chǔ)
- 不支持索引訪問(wèn)
- 延遲計(jì)算
- 支持并行
- 很容易生成數(shù)組或集合(List扣墩,Set)
- 支持過(guò)濾哲银,查找,轉(zhuǎn)換呻惕,匯總荆责,聚合等操作
3、Stream運(yùn)行機(jī)制
Stream分為源Source亚脆、中間操作做院、終止操作。
- 流的源可以是一個(gè)數(shù)組濒持、一個(gè)集合键耕、一個(gè)生成器方法、一個(gè)I/O通道等等弥喉。
- 一個(gè)流可以有零個(gè)或者多個(gè)中間操作郁竟,每一個(gè)中間操作都會(huì)返回一個(gè)新的流玛迄,供下一個(gè)操作使用由境。
- 一個(gè)流只會(huì)有一個(gè)終止操作。
- Stream只有遇到終止操作蓖议,它的源才開(kāi)始執(zhí)行遍歷操作虏杰。
4、Stream的創(chuàng)建
- 通過(guò)數(shù)組
Arrays.stream(T array)
Stream.of(T array)
/**
* 方式一: 通過(guò)數(shù)組生成
*/
@Test
public void testBuildStream1() {
String[] strArr = {"a", "b", "c"};
//static <T> Stream<T> of(T... values) 返回其元素是指定值的順序排序流勒虾。
Stream<String> stream1 = Stream.of(strArr);
stream1.forEach(System.out::println);
//static <T> Stream<T> of(T t) 返回包含單個(gè)元素的順序 Stream 纺阔。
Stream<String> stream2 = Stream.of("d");
stream2.forEach(System.out::println);
Stream<String> stream3 = Arrays.stream(strArr);
stream3.forEach(System.out::println);
}
- 通過(guò)集合
Collection.stream()
Collection.parallelStream():獲取的是并行操作的流
/**
* 方式二: 通過(guò)集合來(lái)生成
*/
@Test
public void testBuildStream2() {
List<String> list = Arrays.asList("a", "b", "c", "d");
Stream<String> stream1 = list.stream();
stream1.forEach(System.out::println);
// parallelStream(): 獲取的是并行操作的流
Stream<String> stream2 = list.parallelStream();
stream2.forEach(System.out::println);
}
- 通過(guò)Stream.generate方法來(lái)創(chuàng)建
/**
* 方式三: 通過(guò)Stream.generate來(lái)生成
*/
@Test
public void testBuildStream3() {
// Stream.generate : 返回?zé)o限順序無(wú)序流,其中每個(gè)元素由提供的 Supplier 修然。 使用該方法的時(shí)候注意要結(jié)合limit來(lái)使用, 否則Stream中元素的個(gè)數(shù)是無(wú)限的
Stream<Integer> generate = Stream.generate(() -> 1);
generate.limit(10).forEach(System.out::println);
}
- 通過(guò)Stream.iterate方法來(lái)創(chuàng)建
/**
* 方式四: 通過(guò)Stream.iterate來(lái)生成
*/
@Test
public void testBuildStream4() {
// Stream.iterate(T seed, UnaryOperator<T> f) : 返回有序無(wú)限連續(xù) Stream由函數(shù)的迭代應(yīng)用產(chǎn)生 f至初始元素 seed 笛钝,產(chǎn)生 Stream包括 seed 质况, f(seed) , f(f(seed)) 玻靡,等
Stream<Integer> iterate = Stream.iterate(1, x -> x + 1);
iterate.limit(10).forEach(System.out::println);
}
- 其它API創(chuàng)建
需要注意的是结榄,對(duì)于基本數(shù)值型,目前有三種對(duì)應(yīng)的包裝類型 Stream:IntStream囤捻、LongStream臼朗、DoubleStream。當(dāng)然我們也可以用 Stream<Integer>蝎土、Stream<Long> >视哑、Stream<Double>,但是 boxing 和 unboxing 會(huì)很耗時(shí)誊涯,所以特別為這三種基本數(shù)值型提供了對(duì)應(yīng)的Stream挡毅。
Java 8 中還沒(méi)有提供其它數(shù)值型Stream,因?yàn)檫@將導(dǎo)致擴(kuò)增的內(nèi)容較多醋拧。而常規(guī)的數(shù)值型聚合運(yùn)算可以通過(guò)上面三種 Stream 進(jìn)行慷嗜。
/**
* 方式五: 其它方式創(chuàng)建Stream
*/
@Test
public void testBuildStream5() {
String str = "abcdef";
IntStream stream = str.chars();
//stream.forEach(System.out::println);
stream.forEach(x -> System.out.println((char) x));
}
5、Stream常用API
5.1丹壕、中間操作
一個(gè)流可以后面跟隨零個(gè)或多個(gè) intermediate 操作庆械。其目的主要是打開(kāi)流,做出某種程度的數(shù)據(jù)映射/過(guò)濾菌赖,然后返回一個(gè)新的流缭乘,交給下一個(gè)操作使用。這類操作都是惰性化的(lazy)琉用,就是說(shuō)堕绩,僅僅調(diào)用到這類方法,并沒(méi)有真正開(kāi)始流的遍歷邑时。
過(guò)濾:filter
@Test
public void testFilter() {
Arrays.asList(1, 2, 3, 4, 5, 6).stream().filter(x -> {
System.out.println("filter里面的方法被執(zhí)行了");
return x % 2 == 0;
}).forEach(System.out::println);
}
去重:distinct
// Stream中間操作distinct 去除Stream中的重復(fù)元素 使用的是Object.equals(Object)
@Test
public void testDistinct() {
Arrays.asList(1, 2, 2, 4, 4, 6).stream().distinct().forEach(System.out::println);
}
排序:sorted
// Stream中間操作sorted 對(duì)Stream中的元素進(jìn)行排序
// sorted(): 按照自然順序排序
// sorted(Comparator<? super T> comparator) : 按照指定的比較器進(jìn)行排序
@Test
public void testSorted() {
Arrays.asList(6, 2, 5, 1, 4, 3).stream().sorted().forEach(System.out::print);
System.out.println();
Arrays.asList(6, 2, 5, 1, 4, 3).stream().sorted((a, b) -> a - b).forEach(System.out::print);
System.out.println();
Arrays.asList(6, 2, 5, 1, 4, 3).stream().sorted(Comparator.comparingInt(a -> a)).forEach(System.out::print);
System.out.println();
Arrays.asList(6, 2, 5, 1, 4, 3).stream().sorted(Integer::compare).forEach(System.out::print);
}
合并流:concat
// Stream中間操作concat 合并流
// concat(Stream<? extends T> a, Stream<? extends T> b)
// 創(chuàng)建一個(gè)懶惰連接的流奴紧,其元素是第一個(gè)流的所有元素,后跟第二個(gè)流的所有元素晶丘。
@Test
public void testConcat() {
Stream.concat(Arrays.asList("a","b","c").stream(), Arrays.asList("d","e","f").stream()).forEach(System.out::println);
}
截仁虻:limit、skip
// Stream中間操作limit 截取
// limit(long maxSize) : 返回由此流的元素組成的流浅浮,截短長(zhǎng)度不能超過(guò) maxSize 沫浆。
@Test
public void testLimit() {
// 截取前2個(gè)原始
Arrays.asList("a","b","c").stream().limit(2).forEach(System.out::println);
}
// Stream中間操作skip 截取
// skip(long n) : 在丟棄流的第一個(gè) n元素后,返回由該流的 n元素組成的流滚秩。
@Test
public void testSkip() {
// 跳過(guò)第一個(gè)原始
Arrays.asList("a","b","c").stream().skip(1).forEach(System.out::println);
}
轉(zhuǎn)換:map/flatMap/
// Stream中間操作map 轉(zhuǎn)換
// map(Function<? super T,? extends R> mapper)
@Test
public void testMap() {
Arrays.asList("a","b","c").stream().map(x -> x.toUpperCase()).forEach(System.out::println);
}
// Stream中間操作flatMap 轉(zhuǎn)換
// flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
// flatMap 把 input Stream 中的層級(jí)結(jié)構(gòu)扁平化专执,就是將最底層元素抽出來(lái)放到一起,最終 output 的新 Stream 里面已經(jīng)沒(méi)有 List 了郁油,都是直接的數(shù)字本股。
@Test
public void testFlatMap() {
Stream<List<Integer>> inputStream = Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4), Arrays.asList(5, 6));
Stream<Integer> stream = inputStream.flatMap(childList -> childList.stream());
stream.forEach(System.out::println);
}
其它:peek
// Stream中間操作peek 查看
@Test
public void testPeek() {
List<User> list = Arrays.asList(new User("zhangsan", 1), new User("lisi", 2));
List<User> collect = list.stream().peek(x -> x.setName("lili")).collect(Collectors.toList());
collect.stream().forEach(System.out::println);
}
5.2攀痊、終止操作
一個(gè)流只能有一個(gè) terminal 操作,當(dāng)這個(gè)操作執(zhí)行后拄显,流就被使用“光”了蚕苇,無(wú)法再被操作。所以這必定是流的最后一個(gè)操作凿叠。Terminal 操作的執(zhí)行涩笤,才會(huì)真正開(kāi)始流的遍歷,并且會(huì)生成一個(gè)結(jié)果盒件。
循環(huán):forEach
計(jì)算:min蹬碧、max、count、average、sum
// Stream終止操作min 求Stream中的最小值
// min(Comparator<? super T> comparator): 根據(jù)提供的 Comparator返回此流的最小元素命雀。
@Test
public void testMin() {
Optional<Integer> min = Arrays.asList(6, 2, 5, 1, 4, 3).stream().min((a, b) -> a - b);
if (min.isPresent()) {
System.out.println(min.get());
}
}
// Stream終止操作max 求Stream中的最大值
// max(Comparator<? super T> comparator) : 根據(jù)提供的 Comparator返回此流的最大元素。
@Test
public void testMax() {
Optional<Integer> max = Arrays.asList(6, 2, 5, 1, 4, 3).stream().max((a, b) -> a - b);
if (max.isPresent()) {
System.out.println(max.get());
}
}
// Stream終止操作count 求Stream中的元素個(gè)數(shù)
// count() : 返回此流中的元素?cái)?shù)罗心。
@Test
public void testCount() {
long count = Arrays.asList(6, 2, 5, 1, 4, 3).stream().count();
System.out.println(count);
}
// IntStream終止操作average() 求Stream中的元素的算術(shù)平均值
@Test
public void testAverage() {
OptionalDouble average = Arrays.asList("6", "2", "5", "1", "4", "3").stream().mapToInt(Integer::parseInt).average();
if (average.isPresent()) {
System.out.println(average.getAsDouble());
}
}
// IntStream終止操作sum() 返回此流中元素的總和。
@Test
public void testSum() {
int sum = Arrays.asList("6", "2", "5", "1", "4", "3").stream().mapToInt(Integer::parseInt).sum();
System.out.println(sum);
}
匹配:anyMatch城瞎、allMatch渤闷、noneMatch、findFirst脖镀、findAny
// Stream終止操作allMatch
// allMatch(Predicate<? super T> predicate) : 返回此流的所有元素是否與提供的謂詞匹配(所有的元素都滿足返回true, 否則返回false)飒箭。
@Test
public void testAllMatch() {
boolean b = Arrays.asList(6, 2, 5, 1, 4, 3).stream().allMatch(x -> x > 0);
System.out.println(b);
}
// Stream終止操作anyMatch
// anyMatch(Predicate<? super T> predicate) : 返回此流的任何元素是否與提供的謂詞匹配(任何一個(gè)元素符合條件則返回true, 否則返回false)。
@Test
public void testAnyMatch() {
boolean b = Arrays.asList(6, 2, 5, 1, 4, 3).stream().anyMatch(x -> x > 5);
System.out.println(b);
}
// Stream終止操作noneMatch
// noneMatch(Predicate<? super T> predicate) : 返回此流的元素是否與提供的謂詞匹配蜒灰。 (所有元素都不匹配則返回true, 否則返回false)弦蹂。
@Test
public void testNoneMatch() {
boolean b = Arrays.asList(6, 2, 5, 1, 4, 3).stream().noneMatch(x -> x > 5);
System.out.println(b);
}
// Stream終止操作findFirst
// findFirst() : 返回描述此流的第一個(gè)元素的Optional如果流為空,則返回一個(gè)空的Optional 强窖。
@Test
public void testFindFirst() {
Optional<Integer> first = Arrays.asList(6, 2, 5, 1, 4, 3).stream().findFirst();
if (first.isPresent()) {
System.out.println(first.get());
}
}
// Stream終止操作findAny
// findAny() : 返回描述流的一些元素的Optional如果流為空凸椿,則返回一個(gè)空的Optional 。
@Test
public void testFindAny() {
Optional<Integer> any = Arrays.asList(6, 2, 5, 1, 4, 3).stream().findAny();
if (any.isPresent()) {
System.out.println(any.get());
}
}
匯聚:reduce
這個(gè)方法的主要作用是把 Stream 元素組合起來(lái)翅溺。它提供一個(gè)起始值(種子)脑漫,然后依照運(yùn)算規(guī)則(BinaryOperator),和前面 Stream 的第一個(gè)未巫、第二個(gè)窿撬、第 n 個(gè)元素組合启昧。從這個(gè)意義上說(shuō)叙凡,字符串拼接、數(shù)值的 sum密末、min握爷、max跛璧、average 都是特殊的 reduce。例如 Stream 的 sum 就相當(dāng)于Integer sum = integers.reduce(0, (a, b) -> a+b); 或Integer sum = integers.reduce(0, Integer::sum);
也有沒(méi)有起始值的情況新啼,這時(shí)會(huì)把Stream的前面兩個(gè)元素組合起來(lái)追城,返回的是 Optional。
// Stream終止操作reduce
@Test
public void testReduce() {
// 字符串連接燥撞,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println(concat);
// 求最小值座柱,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
System.out.println(minValue);
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
System.out.println(sumValue);
// 求和物舒,sumValue = 10, 無(wú)起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println(sumValue);
// 過(guò)濾色洞,字符串連接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
System.out.println(concat);
}
上面代碼例如第一個(gè)示例的 reduce()冠胯,第一個(gè)參數(shù)(空白字符)即為起始值火诸,第二個(gè)參數(shù)(String::concat)為 BinaryOperator。這類有起始值的 reduce() 都返回具體的對(duì)象荠察。而對(duì)于第四個(gè)示例沒(méi)有起始值的 reduce()置蜀,由于可能沒(méi)有足夠的元素,返回的是 Optional悉盆,請(qǐng)留意這個(gè)區(qū)別盯荤。
收集器:toArray、collect
// Stream終止操作toArray焕盟、collect
@Test
public void testToArrayAndCollect() {
List<Integer> collect = Arrays.asList(6, 2, 5, 1, 4, 3).stream().collect(Collectors.toList());
System.out.println(collect);
Object[] objects = Arrays.asList(6, 2, 5, 1, 4, 3).stream().toArray();
Arrays.stream(objects).forEach(System.out::println);
}
在對(duì)于一個(gè)Stream進(jìn)行多次轉(zhuǎn)換操作 (Intermediate 操作)廷雅,每次都對(duì)Stream的每個(gè)元素進(jìn)行轉(zhuǎn)換,而且是執(zhí)行多次京髓,這樣時(shí)間復(fù)雜度就是N(轉(zhuǎn)換次數(shù))個(gè)for循環(huán)里把所有操作都做掉的總和嗎航缀?其實(shí)不是這樣的,轉(zhuǎn)換操作都是lazy的堰怨,多個(gè)轉(zhuǎn)換操作只會(huì)在 Terminal 操作的時(shí)候融合起來(lái)芥玉,一次循環(huán)完成。我們可以這樣簡(jiǎn)單的理解备图,Stream 里有個(gè)操作函數(shù)的集合灿巧,每次轉(zhuǎn)換操作就是把轉(zhuǎn)換函數(shù)放入這個(gè)集合中,在 Terminal 操作的時(shí)候循環(huán) Stream 對(duì)應(yīng)的集合揽涮,然后對(duì)每個(gè)元素執(zhí)行所有的函數(shù)抠藕。
還有一種操作被稱為short-circuiting。用以指:
對(duì)于一個(gè)intermediate操作蒋困,如果它接受的是一個(gè)無(wú)限大(infinite/unbounded)的Stream盾似,但返回一個(gè)有限的新Stream。
對(duì)于一個(gè) terminal 操作雪标,如果它接受的是一個(gè)無(wú)限大的 Stream零院,但能在有限的時(shí)間計(jì)算出結(jié)果溉跃。
當(dāng)操作一個(gè)無(wú)限大的Stream,而又希望在有限時(shí)間內(nèi)完成操作告抄,則在管道內(nèi)擁有一個(gè) short-circuiting 操作是必要非充分條件撰茎。