java8中-_java8中的Stream
java8提出的函數(shù)式編程旨在幫助程序猿們寫(xiě)出更優(yōu)雅的代碼,上文函數(shù)式編程基礎(chǔ)也介紹了java8新提出的一些函數(shù)式接口,通過(guò)它們代碼貌似已經(jīng)簡(jiǎn)潔了一波,但是,代碼其實(shí)還可以更簡(jiǎn)潔下,接下來(lái)就要開(kāi)始給大家介紹另一個(gè)神器了:Stream,通過(guò)它可以進(jìn)一步利用函數(shù)式接口來(lái)簡(jiǎn)化代碼了。
注:Stream是java8核心類(lèi)庫(kù)中新引入API,它使程序猿們站在更高的抽象層次上對(duì)集合進(jìn)行操作。
常用的流操作
流對(duì)象的創(chuàng)建
通過(guò)Stream創(chuàng)建流對(duì)象:
a. 通過(guò)empty方法創(chuàng)建空的流對(duì)象:
public static Stream empty() {
return StreamSupport.stream(Spliterators.emptySpliterator(), false);
}
b. 通過(guò)of方法創(chuàng)建流對(duì)象:
//創(chuàng)建只含有一個(gè)元素的流對(duì)象
public static Stream of(T t) {
return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}
//創(chuàng)建有多個(gè)元素的流對(duì)象
public static Stream of(T... values) {
return Arrays.stream(values);
}
c. 通過(guò)iterate方法創(chuàng)建流對(duì)象:
public static Stream iterate(final T seed, final UnaryOperator f) {
Objects.requireNonNull(f);
final Iterator iterator = new Iterator() {
@SuppressWarnings("unchecked")
T t = (T) Streams.NONE;
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() {
return t = (t == Streams.NONE) ? seed : f.apply(t);
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
iterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}
從源碼可以看出,該方法會(huì)返回一個(gè)無(wú)限有序的Stream對(duì)象,它的第一個(gè)元素是seed,第二個(gè)元素是f.apply(seed)...第n個(gè)元素是f.apply(n - 1元素的值),它一般與limit等方法一起使用,來(lái)獲取前多少個(gè)元素。
我們來(lái)看看它是如何和limit方法搭配使用的:
//定義一個(gè)第一個(gè)元素 = 10, 后一個(gè)元素 = 前一個(gè)元素 + 2, 長(zhǎng)度為20的集合并輸出
Stream.iterate(10, n -> n + 2).limit(20).forEach(System.out::println);
d. 通過(guò)generate方法創(chuàng)建流對(duì)象,它跟iterate方法有點(diǎn)類(lèi)似,但是它的每個(gè)元素都與前一個(gè)元素沒(méi)有什么關(guān)系,并且無(wú)序,多用于創(chuàng)建常量Stream或者隨機(jī)Stream,我們來(lái)看看它是如何使用的:
//定義一個(gè)長(zhǎng)度為10的隨機(jī)字符串集合并輸出
Stream.generate(() -> UUID.randomUUID().toString()).limit(10).forEach(System.out::println);
e. 通過(guò)concat方法連接兩個(gè)Stream對(duì)象生成一個(gè)新的Stream對(duì)象:
//定義一個(gè)長(zhǎng)度為3的隨機(jī)字符串集合
Stream stream1 = Stream.generate(() -> UUID.randomUUID().toString()).limit(3);
//定義一個(gè)第一個(gè)元素 = 0, 后一個(gè)元素 = 前一個(gè)元素 + 2, 長(zhǎng)度為3的集合
Stream stream2 = Stream.iterate(0, n -> n + 2).limit(3);
//連接stream1和stream2, 遍歷輸出
Stream.concat(stream1, stream2).forEach(System.out::println);
我們來(lái)看下運(yùn)行結(jié)果:
ccf394b2-513c-4d4e-8aa4-b824c95918f4
907b0793-fa19-4cc7-8292-7b7e0818b0a4
a4b8d4a4-80b2-47e0-973e-80694d355ab2
0
2
4
通過(guò)Collection的stream方法和parallelStream方法創(chuàng)建串行或并行的流對(duì)象:
//創(chuàng)建串行流對(duì)象
default Stream stream() {
return StreamSupport.stream(spliterator(), false);
}
//創(chuàng)建并行流對(duì)象
default Stream parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
我們來(lái)看看具體使用:
List list = Arrays.asList(1, 2, 3, 4, 5);
//創(chuàng)建串行流對(duì)象
Stream stream = list.stream();
//創(chuàng)建并行流對(duì)象
Stream parallelStream = list.parallelStream();
流對(duì)象的創(chuàng)建基本就這些方式創(chuàng)建了,知道怎么創(chuàng)建了,我們接下來(lái)看看流常用的方法。
Stream常用方法
map
元素一對(duì)一轉(zhuǎn)換,如果你想將一種類(lèi)型的值轉(zhuǎn)換成另一種類(lèi)型的值可以用該方法,它的定義如下:
Stream map(Function super T, ? extends R> mapper);
從定義可以看出,它接受一個(gè)Function參數(shù),元素相關(guān)處理轉(zhuǎn)換處理都在Function里實(shí)現(xiàn),同時(shí)將處理后的結(jié)果放在新的Stream對(duì)象中返回。另外,Stream也提供方法針對(duì)轉(zhuǎn)換成基本類(lèi)型(int,long,double)場(chǎng)景:
//轉(zhuǎn)換成int類(lèi)型
IntStream mapToInt(ToIntFunction super T> mapper);
//轉(zhuǎn)換成long類(lèi)型
LongStream mapToLong(ToLongFunction super T> mapper);
//轉(zhuǎn)換成double類(lèi)型
DoubleStream mapToDouble(ToDoubleFunction super T> mapper);
我們來(lái)看看它是如何使用的:
//定義一個(gè)第一個(gè)元素 = "1", 后一個(gè)元素 = 前一個(gè)元素 + "2", 長(zhǎng)度為10的集合
List origins = Stream.iterate("1", n -> n + "2").limit(10).collect(Collectors.toList());
//將origins中的字符串轉(zhuǎn)換成int類(lèi)型并輸出
origins.stream().mapToInt(value -> Integer.valueOf(value)).forEach(System.out::println);
//將origins中的字符串轉(zhuǎn)換成User類(lèi)型并輸出
origins.stream().map(value -> new User(value)).forEach(System.out::println);
flatMap
Stream flatMap(Function super T, ? extends Stream extends R>> mapper);
可以理解成元素一對(duì)多轉(zhuǎn)換,從定義可以看出,Stream對(duì)象中的每個(gè)元素經(jīng)過(guò)Function處理后變成一個(gè)多元素的Stream對(duì)象,然后將多個(gè)Stream拼接成一個(gè)新的Stream對(duì)象返回。我們來(lái)個(gè)例子:假如我們有一個(gè)字符串列表:["I", "Love", "Programing"],要將該單詞列表轉(zhuǎn)換成:["I", "L", "o", "v", "e", "P", "r", "o", "g", "r", "a", "m", "i", "n", "g"]:
Stream origins = Stream.of("I", "Love", "Programing");
//字符串轉(zhuǎn)換
List characters = origins.flatMap(value -> Stream.of(value.split(""))).collect(Collectors.toList());
System.out.println(characters);
我們看看輸出結(jié)果:
[I, L, o, v, e, P, r, o, g, r, a, m, i, n, g]
filter
元素過(guò)濾,將符合一定條件的元素過(guò)濾出來(lái),它的定義如下:
Stream filter(Predicate super T> predicate);
從定義可以看出,它接受一個(gè)Predicate參數(shù),元素條件判斷都放在Predicate里,同時(shí)將符合條件的元素放在新的Stream對(duì)象中返回。我們來(lái)看看它的使用:
Stream origins = Stream.of("I", "Love", "Programing");
//輸出包含"o"的字符串
origins.filter(value -> value.contains("o")).forEach(System.out::println);
match
判斷是否有元素符合一定條件,Stream主要提供以下三個(gè)方法來(lái)實(shí)現(xiàn)該操作:
//部分符合,只要有一個(gè)元素符合條件時(shí)就返回true
boolean anyMatch(Predicate super T> predicate);
//全部符合,所有元素符合條件才返回true
boolean allMatch(Predicate super T> predicate);
//全部不符合,所有元素都不符合條件時(shí)返回true
boolean noneMatch(Predicate super T> predicate);
collect
在上面的例子中總是能看到將Stream對(duì)象轉(zhuǎn)換成集合的時(shí)候用了該方法,是一個(gè)及早求值操作。其實(shí)它的用法遠(yuǎn)遠(yuǎn)不止這些,其他用法后續(xù)會(huì)出文單獨(dú)分析,這里就不詳加贅述了
注:及早求值與惰性求值
判斷及早求值與惰性求值很簡(jiǎn)單,只需要看返回值,如果返回值是Stream,就是惰性求值,如果返回值是另外的值或者為空,則為及早求值。使用流的各種操作的理想方式就是形成一個(gè)惰性求值的鏈,最后用一個(gè)及早求值的操作返回想要的結(jié)果。只有在對(duì)需要什么樣的結(jié)果和操作有一定的了解之后才能更有效的作出正確計(jì)算。
reduce
從一組值生成另一組值,count/min/max其實(shí)都是reduce的操作。我們來(lái)個(gè)小例子:求和
Stream origins = Stream.of(1, 2, 3, 4, 5);
//求和
int sum = origins.reduce(0, (m, n) -> m + n);
System.out.println(sum);
來(lái)個(gè)圖我們來(lái)形象的展示下通過(guò)reduce進(jìn)行累加的一個(gè)過(guò)程:
reduce累加求和示意圖
以0作為起點(diǎn),每一次都將Stream中的元素累加到accumulator,遍歷累加到最后一個(gè)元素,accumulator里的值其實(shí)就是最后的結(jié)果。
到這里為止,常用的一些流操作基本上就介紹完了,還有一些去重,排序,查找等簡(jiǎn)單的內(nèi)容這里就不做贅述了,有感興趣的同學(xué)可以自行翻閱源碼,用法也很簡(jiǎn)單。
后記
從java8之前的集合操作到j(luò)ava8 Stream對(duì)集合的操作,代碼更加簡(jiǎn)單、易讀,這一巨大的改變其實(shí)是依賴(lài)外部迭代 -> 內(nèi)部迭代這個(gè)轉(zhuǎn)變。比如我們遍歷求和,java8之前的代碼會(huì)這樣寫(xiě):
int sum = 0;
for (Integer number : numbers) {
sum += number;
}
雖然這樣做其實(shí)也不會(huì)有問(wèn)題,但是還是要正面它的不好處:
每次對(duì)集合做遍歷都要寫(xiě)一堆這樣子的代碼;
比較難抽象操作,操作和遍歷都糅合在一起;
在for循環(huán)的模式下再進(jìn)行性能改造其實(shí)比較困難(比如并行);
代碼易讀性較差,特別是有多重嵌套循環(huán)時(shí),看代碼的人估計(jì)就要崩潰了。
當(dāng)然咯,for循環(huán)就是一個(gè)封裝了迭代(Iterator)的語(yǔ)法糖,這種遍歷模式其實(shí)也就是外部迭代。
何謂外部迭代,簡(jiǎn)單點(diǎn)就是顯式地進(jìn)行迭代操作,比如通過(guò)Iterator進(jìn)行迭代,它的過(guò)程就是顯式調(diào)用Iterator對(duì)象的hasNext和next方法完成迭代。
那現(xiàn)在我們通過(guò)Stream將其變成內(nèi)部迭代呢:
Stream origins = Stream.of(1, 2, 3, 4, 5);
//求和
int sum = origins.reduce(0, (m, n) -> m + n);
所謂的內(nèi)部迭代,顧名思義,遍歷會(huì)在集合內(nèi)部完成,程序猿不需要再去顯示的控制循環(huán),也不再需要關(guān)心遍歷元素的順序,我們只需要care對(duì)元素的操作即可。
總結(jié)
以上是生活随笔為你收集整理的java8中-_java8中的Stream的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: linux无法访问mysql_Linux
- 下一篇: predicate java_java代