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