Java8中Stream詳解

1.什麼是Stream

Stream中文稱爲,通過將集合轉換爲這麼一種叫做"流"的元素序列,通過聲明性方式,能夠對集合中的每個元素進行一系列並行或串行的流水線操作

Stream是用函數式編程方式在集合類上進行復雜操作的工具。

2.創建Stream

2.1Stream接口的靜態工廠方法

2.1.1of

of方法,其生成的Stream是有限長度的,Stream的長度爲其內的元素個數。

方法 描述
of(T… values) 返回含有多個T元素的Stream
of(T t) 返回含有一個T元素的Stream

示例:

Stream<String> hello = Stream.of("hello", "world", "stream");

2.1.2iterate

iterate方法,其返回的是一個無限長度有序的Stream,其是通過函數f迭代應用於指定的元素種子seed,生成由seedf(seed),f(f(seed))以此類推等組成的Stream。

iterate(final T seed, final UnaryOperator<T> f)

示例:

Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(10);

上面示例,種子爲0,可認爲該Stream的第一個元素,通過f函數來產生第二個元素。接着,第二個元素,作爲產生第三個元素的種子,從而產生了第三個元素,以此類推下去。需要注意的是,該Stream是無限長度的,應該使用filter、limit等來截取Stream,否則會一直循環下去。

2.1.3generator

generator方法,返回一個無限長度無序的Stream,其元素由Supplier接口提供。

generate(Supplier<T> s)

1.這種情形通常用於隨機數、常量的 Stream,或者需要前後元素間維持着某種狀態信息的Stream。

2.把Supplier實例傳遞給Stream.generate()生成的Stream,默認是串行(相對parallel而言)但無序的(相對ordered而言)。

示例:

Stream<Double> doubleStream = Stream.generate(Math::random).limit(10);

2.1.4empty

empty方法返回一個空的順序Stream,該Stream裏面不包含元素項。

示例:

Stream<String> emptyStream = Stream.empty();

2.1.5構建器builder()

builder方法返回一個Stream的構建器。

返回類型 方法 描述
Stream.Builder<T> builder() 返回Stream的構建器

Stream.Builder<T>

Stream的可變構建器。它允許通過逐一生成元素並將它們添加到Builder來創建Stream(沒有使用ArrayList作爲臨時緩衝區的複製開銷)
Stream流構建器具有生命週期,其從構建階段開始,在該階段期間可以添加元素,然後轉換到內置階段,之後可能不添加元素。內建階段是在調用build()方法時開始的,它創建一個有序的Stream,其元素是添加到流構建器的元素,按添加順序排列。

修飾符和類型 方法和描述
void accept(T t)向正在構建的流添加元素。
default Stream.Builder<T> add(T t)向正在構建的流添加元素。
Stream<T> build()構建流,將此構建器轉換爲內置/構建狀態。

示例:

Stream.Builder<String> builder = Stream.builder();
builder.add("apple").add("banana");
builder.accept("orange");
builder.add("peach");
Stream<String> stringStream = builder.build();
stringStream.forEach(System.out::println);
// apple
// banana
// orange
// peach

2.2集合和數組

2.2.1集合

Collection接口的默認方法,把一個Collection對象轉換成Stream

在java.util.Collection接口中定義了默認方法stream()parallelStream(),用來生成一個Stream。

default Stream<E> stream() {
	return StreamSupport.stream(spliterator(), false);
}

default Stream<E> parallelStream() {
	return StreamSupport.stream(spliterator(), true);
}

示例:

List<String> list = Arrays.asList("hello", "world", "stream");
Stream<String> stream = list.stream();

2.2.2數組

在java.util.Arrays類中封裝了一系列的stream()方法,不僅針對於任何類型的元素採用了泛型,更對於基本類型做了相應封裝,以便提升Stream的處理效率。

數組中stream方法

示例:

String[] strings = {"hello", "world", "stream"};
Stream<String> stream = Arrays.stream(strings);

2.3其他

從文件讀取流

示例:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

Path path = Paths.get("C:\\Users\\CreateStream.java");
Stream<String> streamFromFile = Files.lines(path);

Random.ints()
BufferedReader.lines()
BitSet.stream()
Pattern.splitAsStream(java.lang.CharSequence)
JarFile.stream()

3.操作分類

流的操作類型分爲兩種:

  • Intermediate:一個流可以後面跟隨零個或多個intermediate操作。其目的主要是打開流,做出某種程度的數據映射/過濾,然後返回一個新的流,交給下一個操作使用。這類操作都是惰性化的(lazy),就是說,僅僅調用到這類方法,並沒有真正開始流的遍歷。
  • Terminal:一個流只能有一個terminal操作,當這個操作執行後,流就被使用"光"了,無法再被操作。所以這必定是流的最後一個操作。Terminal操作的執行,纔會真正開始流的遍歷,並且會生成一個結果,或者一個side effect。

還有一種操作被稱爲short-circuiting。用以指:

  • 對於一個intermediate操作,如果它接受的是一個無限大(infinite/unbounded)的Stream,但返回一個有限的新Stream。
  • 對於一個terminal操作,如果它接受的是一個無限大的Stream,但能在有限的時間計算出結果。

當操作一個無限大的Stream,而又希望在有限時間內完成操作,則在管道內擁有一個short-circuiting操作是必要非充分條件。

序號 操作 類型 返回類型 參數
1 concat intermediate Stream<T> Stream<T>, Stream<T>
2 distinct stateful intermediate Stream<T>
3 filter intermediate Stream<T> Predicate<T>
4 map intermediate Stream<R> Function<T, R>
5 flatMap intermediate Stream<R> Function<T, R>
6 peek intermediate Stream<T> Consumer<T>
7 skip stateful intermediate Stream<T> long
8 sorted stateful intermediate Stream<T> [Comparator<T>]
9 parallel intermediate Stream<T>
10 sequential intermediate Stream<T>
11 unordered intermediate Stream<T>
12 forEach terminal void Consumer<T>
13 forEachOrdered terminal void Consumer<T>
14 toArray terminal Object[] [IntFunction<A[]>]
15 reduce terminal Optional<T>
T
U
BinaryOperator<T>
T, BinaryOperator<T>
U, BiFunction<U, T, U>, BinaryOperator<U>
16 collect terminal R Collector<T, A, R>
Supplier<R>, BiConsumer<R, T>, BiConsumer<R, R>
17 min terminal Optional<T> Comparator<T>
18 max terminal Optional<T> Comparator<T>
19 count terminal long
20 iterator terminal Stream<T> final T, final UnaryOperator<T>
21 limit short-circuiting stateful intermediate Stream<T> long
22 anyMatch short-circuiting intermediate boolean Predicate<T>
23 allMatch short-circuiting intermediate boolean Predicate<T>
24 noneMatch short-circuiting intermediate boolean Predicate<T>
25 findFirst short-circuiting intermediate Optional<T>
26 findAny short-circuiting intermediate Optional<T>

3.1中間操作

Intermediate主要是用來對Stream做出相應轉換及限制流,實際上是將源Stream轉換爲一個新的Stream,以達到需求效果。

3.1.1concat

concat方法將兩個Stream連接在一起,合成一個Stream。若兩個輸入的Stream都是排序的,則新Stream也是排序的;若輸入的Stream中任何一個是並行的,則新的Stream也是並行的;若關閉新的Stream時,原兩個輸入的Stream都將執行關閉處理。

示例:

Stream<Integer> concatStream = Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5));

3.1.2distinct

distinct方法以達到去除掉原Stream中重複的元素,生成的新Stream中沒有重複的元素。

For ordered streams, the selection of distinct elements is stable (for duplicated elements, the element appearing first in the encounter order is preserved.) For unordered streams, no stability guarantees are made.

對於有序流,不同元素的選擇是穩定的(對於重複元素,將保留最先出現在遭遇順序中的元素)。對於無序流,不提供穩定性保證。

示例:

Stream.of(1, 2, 3, 4, 1, 2, 3).distinct().forEach(System.out::println);
// 1
// 2
// 3
// 4

3.1.3filter

filter方法對原Stream按照指定條件過濾,在新建的Stream中,只包含滿足條件的元素,將不滿足條件的元素過濾掉。

示例:

Stream.of(1, 2, 3, 4).filter(item -> item % 2 == 0).forEach(System.out::println);
// 2
// 4

3.1.4map

map方法將對於Stream中包含的元素使用給定的轉換函數進行轉換操作,新生成的Stream只包含轉換生成的元素。爲了提高處理效率,官方已封裝好了,三種變形:mapToDoublemapToIntmapToLong。如果想將原Stream中的數據類型,轉換爲double,int或者是long可以調用相對應的方法。

示例:

Stream.of(1, 2, 3).map(item -> "result:" + (item - 1)).forEach(System.out::println);
// result:0
// result:1
// result:2

3.1.5flatMap

flatMap方法與map方法類似,都是將原Stream中的每一個元素通過轉換函數進行轉換,不同的是,該換轉函數的對象是一個Stream,也不會再創建一個新的Stream,而是將原Stream的元素取代爲轉換的Stream。如果轉換函數生成的Stream爲null,應由空Stream取代。flatMap有三個對於原始類型的變種方法,分別是:flatMapToDoubleflatMapToIntflatMapToLong

注意: flatMap操作的效果是對流的元素應用一對多的轉換,然後將生成的元素扁平爲新的流。

Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

示例1:

String[] words = {"Hello", "World"};
// [H, e, l, l, o],[W, o, r, l, d]
Stream<String[]> stream = Arrays.stream(words).map(w -> w.split(""));
Stream<String> stringStream = stream.flatMap(Arrays::stream);
stringStream.forEach(System.out::print); // HelloWorld

示例2:

Stream.of(1, 2, 3).flatMap(a -> Stream.of(a + 1)).forEach(System.out::println);
// 2
// 3
// 4

flatMap傳入的Lambda表達式必須是Function實例,參數可以爲任意類型,而其返回值類型必須是一個Stream。

3.1.6peek

peek方法生成一個包含原Stream的所有元素的新Stream,同時會提供一個消費函數(Consumer),新Stream每個元素被消費的時候都會執行給定的消費函數。

示例:

Stream.of("one", "two", "three", "four")        
    .filter(e -> e.length() > 3)        
    .peek(e -> System.out.println("Filtered value: " + e))
    .map(String::toUpperCase)        
    .peek(e -> System.out.println("Mapped value: " + e))
    .collect(Collectors.toList());
// Filtered value: three
// Mapped value: THREE
// Filtered value: four
// Mapped value: FOUR

注意:

This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline.

此方法主要用於支持調試,當你想要在元素流過管道中的某個點時看到這些元素。

3.1.7skip

skip方法將過濾掉原Stream中的前N個元素,返回剩下的元素所組成的新Stream。如果原Stream的元素個數大於N,將返回原Stream的後(原Stream長度-N)個元素所組成的新Stream;如果原Stream的元素個數小於或等於N,將返回一個空Stream。

示例:

Stream.of(1, 2, 3, 4, 5).skip(2).forEach(System.out::println);
// 3
// 4
// 5

3.1.8sorted

sorted方法將對原Stream進行排序,返回一個有序列的新Stream。sorted有兩種變體sorted(),sorted(Comparator),前者將默認使用Object.equals(Object)進行排序,而後者接受一個自定義排序規則函數(Comparator),可按照意願排序。

示例:

Stream.of(5, 4, 3, 2, 1)        
	.sorted()        
	.forEach(System.out::println);
// 1
// 2
// 3
// 4
// 5

Stream.of(1, 3, 4, 2, 5)
    .sorted(Comparator.comparingInt(a -> -a))
    .forEach(System.out::println);
// 5
// 4
// 3
// 2
// 1

3.1.9parallel

parallel方法返回並行的等效流。可能會返回自身,因爲流已經並行,或者基礎(底層)流狀態被修改爲並行。

並行流就是一個把內容分成多個數據塊,並用不同的線程分別處理每個數據塊的流。

對順序流調用parallel方法並不意味着流本身有任何實際的變化。它在內部實際上就是設了一個boolean標誌,表示想讓調用parallel之後進行的所有操作都並行執行。類似地,只需要對並行流調用sequential方法就可以把它變成順序流。
請注意,你可能以爲把這兩個方法結合起來,就可以更細化地控制在遍歷流時哪些操作要並行執行,哪些要順序執行。
但最後一次parallelsequential調用會影響整個流水線。

stream.parallel()
    .filter(...)
    .sequential()
    .map(...)
    .parallel()
    .reduce();

在本例中,流水線會並行執行,因爲最後調用的是它。

配置並行流使用的線程池

並行流內部使用了默認的ForkJoinPool,它默認的線程數量就是你的處理器數量,這個值是由Runtime.getRuntime().available
Processors()得到的。
可以通過系統屬性java.util.concurrent.ForkJoinPool.common.parallelism來改變線程池大小,如下所示:
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "12");
這是一個全局設置,因此它將影響代碼中所有的並行流。反過來說,目前還無法專爲某個並行流指定這個值。一般而言,讓ForkJoinPool的大小等於處理器數量是個不錯的默認值,強烈建議不要修改它。

3.1.9.1高效使用並行流

  • 如果有疑問,測量。並行流並不總是比順序流快。此外,並行流有時候會和你的直覺不一致,所 以在考慮選擇順序流還是並行流時,第一個也是最重要的建議就是用適當的基準來檢查其性能。

  • 留意裝箱。自動裝箱和拆箱操作會大大降低性能。Java 8中有原始類型流(IntStream、LongStream、DoubleStream)來避免這種操作,但凡有可能都應該用這些流。

  • 有些操作本身在並行流上的性能就比順序流差。特別是limitfindFirst等依賴於元素順序的操作,它們在並行流上執行的代價非常大。例如,findAny會比findFirst性能好,因爲它不一定要按順序來執行。總是可以調用unordered方法來把有序流變成無序流。那麼,如果需要流中的n個元素而不是專門要前n個的話,對無序並行流調用limit可能會比單個有序流(比如數據源是一個List)更高效。

  • 還要考慮流的操作流水線的總計算成本。設N是要處理的元素的總數,Q是一個元素通過流水線的大致處理成本,則N*Q就是這個對成本的一個粗略的定性估計。Q值較高就意味着使用並行流時性能好的可能性比較大。

  • 對於較小的數據量,選擇並行流幾乎從來都不是一個好的決定。並行處理少數幾個元素的好處還抵不上並行化造成的額外開銷。

  • 要考慮流背後的數據結構是否易於分解。例如,ArrayList的拆分效率比LinkedList高得多,因爲前者用不着遍歷就可以平均拆分,而後者則必須遍歷。另外,用range工廠方法創建的原始類型流也可以快速分解。可以自己實現Spliterator來完全掌控分解過程。

  • 流自身的特點,以及流水線中的中間操作修改流的方式,都可能會改變分解過程的性能。
    例如,一個SIZED流可以分成大小相等的兩部分,這樣每個部分都可以比較高效地並行處理,但篩選操作可能丟棄的元素個數卻無法預測,導致流本身的大小未知。

  • 還要考慮終端操作中合併步驟的代價是大是小(例如Collector中的combiner方法)。如果這一步代價很大,那麼組合每個子流產生的部分結果所付出的代價就可能會超出通過並行流得到的性能提升。

按照可分解性總結了一些流數據源適不適於並行操作

Stream源 可分解性
ArrayList Excellent極佳
LinkedList Poor差
IntStream.range Excellent極佳
Stream.iterate Poor差
HashSet Good好
TreeSet Good好

3.2終端操作

3.2.1collect

collect方法用於收集

List<Integer> collect = Stream.of(1, 2, 3, 4).collect(Collectors.toList());
System.out.println(collect); // [1, 2, 3, 4]

具體請參考Java8中Collectors語法詳解

3.2.2count

count方法將返回Stream中元素的個數。

示例:

long count = Stream.of(1, 2, 3, 4, 5).count();
System.out.println("count:" + count); // count:5

3.2.3forEach

forEach方法用於遍歷Stream中的所有元素。

Stream.of(3, 2, 1).forEach(System.out::println);
// 3
// 2
// 1

3.2.4forEachOrdered

forEachOrdered方法與forEach類似,都是遍歷Stream中的所有元素,不同的是,如果該Stream預先設定了順序,會按照預先設定的順序執行(Stream是無序的),默認爲元素插入的順序。

Stream.of(1,3,2).forEachOrdered(System.out::println);
// 1
// 3
// 2

3.2.5max

max方法根據指定的Comparator,返回一個Optional,該Optional中的value值就是Stream中最大的元素。

Optional<Integer> max = Stream.of(1, 2, 3).max(Integer::compareTo);
System.out.println("max:" + max.get()); // max:3

3.2.6min

min方法根據指定的Comparator,返回一個Optional,該Optional中的value值就是Stream中最小的元素。

Optional<Integer> min = Stream.of(1, 2, 3).min(Integer::compareTo);
System.out.println("min:" + min.get()); // min:1

3.2.7reduce

reduce方法的主要作用是把Stream元素組合起來。它提供一個起始值(種子),然後依照運算規則(BinaryOperator),和前面Stream的第一個、第二個、第n個元素組合。從這個意義上說,字符串拼接、數值的sum、min、max、average都是特殊的reduce。

Integer result = Stream.of(1, 2, 3).reduce(0, (a, b) -> a + b);
System.out.println(result);

Optional<Integer> optionalInteger = Stream.of(1, 2, 3).reduce(Integer::sum);
System.out.println(optionalInteger.get());

reduce有三個變形的方法:

①Optional<T> reduce(BinaryOperator<T> accumulator);

②T reduce(T identity, BinaryOperator<T> accumulator);<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

Optional<T> reduce(BinaryOperator<T> accumulator);

此方法沒有起始值,傳入二元操作符BinaryOperator,第一個參數是上次函數執行的返回值(中間結果),第二個參數是Stream中的元素,執行指定的操作,得到的結果會被賦值給下次執行這個函數的第一個參數。這個方法的返回值類型是Optional。

注意: 第一次執行的時候第一個參數的值是Stream中的第一個元素,第二個參數是Stream中的第二個元素。

Optional<Integer> accumulatorResult = Stream.of(1, 2, 3, 4)        
    .reduce((accumulator, item) -> {            
        System.out.println("accumulator: " + accumulator);            
        accumulator += item;            
        System.out.println("item: " + item);            
        System.out.println("accumulator+: " + accumulator);
        System.out.println("--------");            
        return accumulator;        
    });
System.out.println("accumulatorResult: " + accumulatorResult.get());

accumulator: 1
item: 2
accumulator+: 3
--------
accumulator: 3
item: 3
accumulator+: 6
--------
accumulator: 6
item: 4
accumulator+: 10
--------
accumulatorResult: 10

T reduce(T identity, BinaryOperator<T> accumulator);

此方法指定了起始值,如果Stream爲空,就直接返回該值;由於方法指定了起始值,直接返回具體的對象,不會返回Optional。

Integer accumulatorResult = Stream.of(1, 2, 3, 4)        
    .reduce(0, (accumulator, item) -> {            
        System.out.println("accumulator: " + accumulator);            
        accumulator += item;            
        System.out.println("item: " + item);            
        System.out.println("accumulator+: " + accumulator);
        System.out.println("--------");            
        return accumulator;        
    });
System.out.println("accumulatorResult: " + accumulatorResult);

accumulator: 0
item: 1
accumulator+: 1
--------
accumulator: 1
item: 2
accumulator+: 3
--------
accumulator: 3
item: 3
accumulator+: 6
--------
accumulator: 6
item: 4
accumulator+: 10
--------
accumulatorResult: 10

1.未定義初始值,從而第一次執行的時候第一個參數的值是Stream的第一個元素,第二個參數是Stream的第二個元素。
2.定義了初始值,從而第一次執行的時候第一個參數的值是初始值,第二個參數是Stream的第一個元素。

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

由於reduce變形的第一個參數類型是實際返回實例的數據類型,同時其爲一個泛型也就是意味着該變形可以返回任意類型的數據。

第一個參數返回實例u,傳遞要返回的U類型對象的初始化實例u,第二個參數累加器accumulator,可以使用二元Lambda表達式,聲明在u上操作處理數據來源t的邏輯,第三個參數組合器combiner,同樣是二元操作表達式。

並行操作並且Stream流元素多於1個纔會執行第三個參數的combiner組合器函數,返回組合器函數結果,非並行不會執行第三個參數接口,執行完第二個參數接口後就會返回accumulator累加器的結果。

The {@code identity} value must be an identity for the combiner function. This means that for all {@code u}, {@code combiner(identity, u)} is equal to {@code u}. Additionally, the {@code combiner} function must be compatible with the {@code accumulator} function; for all {@code u} and {@code t}, the following must hold:

combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)

{@code identity}值必須是組合器函數的標識。 這意味着對於所有{@code u},{@code combiner(identity, u)}等於{@code u}。 此外,{@code combiner}函數必須與{@code accumulator}函數兼容; 對於所有{@code u}和{@code t},必須滿足以下條件:

combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)

此處不易理解,以並行計算sum的和爲例

Integer sumReduce = Stream.of(1, 3, 5).parallel().reduce(0,
        (u, t) -> {
            System.out.println("2u:" + u);
            System.out.println("2t:" + t);
            u += t;
            System.out.println("2sum:" + u);
            return u;
        }
        , (u1, u2) -> {
            System.out.println("3u1:" + u1);
            System.out.println("3u2:" + u2);
            u1 += u2;
            System.out.println("3sum:" + u1);
            return u1;
        });
System.out.println("sumReduce:" + sumReduce);
      
2u:0
2t:3
2sum:3
2u:0
2t:5
2sum:5
3u1:3
3u2:5
3sum:8
2u:0
2t:1
2sum:1
3u1:1
3u2:8
3sum:9
sumReduce:9

由於是並行計算,每次返回的結果順序可能是不同的,但sumReduce的值是相同的。組合器函數combiner使用的是BinaryOperator二元操作符,此接口函數提供兩個相同類型參數,並且返回結果與輸入參數類型一致的結果,可以看出accumulator累加器計算的是初始值0+Stream單個item的值,u1和u2傳入的是累加器accumulator函數計算後的結果,因爲要計算sum和,所以combiner組合函數執行的操作是將兩個參數累加。

初始值如果設置1進行並行計算,則累加器計算的是初始值1+Stream單個item的值

Integer sumReduce = Stream.of(1, 3, 5).parallel().reduce(1,
        (u, t) -> {
            System.out.println("2u:" + u);
            System.out.println("2t:" + t);
            u += t;
            System.out.println("2sum:" + u);
            return u;
        }
        , (u1, u2) -> {
            System.out.println("3u1:" + u1);
            System.out.println("3u2:" + u2);
            u1 += u2;
            System.out.println("3sum:" + u1);
            return u1;
        });
System.out.println("sumReduce:" + sumReduce);

2u:1
2t:3
2sum:4
2u:1
2t:5
2sum:6
3u1:4
3u2:6
3sum:10
2u:1
2t:1
2sum:2
3u1:2
3u2:10
3sum:12
sumReduce:12

以下如果不進行並行計算,可以看出非並行不會執行第三個參數接口,執行完第二個參數接口後就會返回accumulator累加器的結果,並且此方式相當於reduce變形②T reduce(T identity, BinaryOperator<T> accumulator);

Integer sumReduce = Stream.of(1, 3, 5).reduce(1,
        (u, t) -> {
            System.out.println("2u:" + u);
            System.out.println("2t:" + t);
            u += t;
            System.out.println("2sum:" + u);
            return u;
        }
        , (u1, u2) -> {
            System.out.println("3u1:" + u1);
            System.out.println("3u2:" + u2);
            u1 += u2;
            System.out.println("3sum:" + u1);
            return u1;
        });
System.out.println("sumReduce:" + sumReduce);

2u:1
2t:1
2sum:2
2u:2
2t:3
2sum:5
2u:5
2t:5
2sum:10
sumReduce:10

3.2.8toArray

toArray方法返回一個包含此Stream流的元素的數組。

返回類型 方法
Object[] toArray()
A[] toArray(IntFunction<A[]> generator)

toArray(IntFunction<A[]> generator)返回一個包含此流元素的數組,使用提供的generator函數分配返回的數組,以及分區執行或調整大小可能需要的任何其它數組。
注意:generator函數採用整數,該大小是所需數組的大小,並生成所需大小的數組;可以用數組構造函數引用簡潔地表達。

int[] intArray = IntStream.rangeClosed(1, 10).toArray();
System.out.println(Arrays.toString(intArray));

String[] strArray = IntStream.rangeClosed(1, 10).mapToObj(String::valueOf)
    .toArray(String[]::new);
System.out.println(Arrays.toString(strArray));
// String類型數組轉換爲Integer類型數組
Integer[] integersArray = Arrays.stream(strArray).mapToInt(Integer::valueOf)
    .boxed().toArray(Integer[]::new);
System.out.println(Arrays.toString(integersArray));

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

3.3短路

3.3.1allMatch

allMatch方法用於判斷Stream中的元素是否全部滿足指定條件。如果全部滿足條件返回true,否則返回false。

示例:

boolean allMatch = Stream.of(1, 2, 3, 4)        
	.allMatch(item -> item > 0);
System.out.println("allMatch: " + allMatch); // allMatch: true

3.3.2anyMatch

anyMatch方法用於判斷Stream中是否有滿足指定條件的元素。如果最少有一個滿足條件返回true,否則返回false。

示例:

boolean anyMatch = Stream.of(1, 2, 3, 4)        
	.anyMatch(item -> item > 3);
System.out.println("anyMatch: " + anyMatch); // anyMatch: true

3.3.3noneMatch

noneMatch方法將判斷Stream中的所有元素是否滿足指定的條件。如果所有元素都不滿足條件,返回true;否則,返回false。

boolean noneMatch = Stream.of(1, 2, 3, 4, 5)        
    .noneMatch(item -> item > 10);
System.out.println("noneMatch:" + noneMatch); // noneMatch:true

boolean noneMatchF = Stream.of(1, 2, 3, 4, 5)        
    .noneMatch(item -> item < 3);
System.out.println("noneMatchF:" + noneMatchF); // noneMatchF:false

3.3.4findAny

findAny方法用於獲取含有Stream中的某個元素的Optional,如果Stream爲空,則返回一個空的Optional。

此操作的行爲明確是不確定的,它會自由的選擇Stream中的任何元素。這是爲了在並行操作中獲得最大的性能;代價是同一個源上的多個調用可能不會返回相同的結果。(如果需要穩定的結果,請使用findFirst())

示例:

Optional<Integer> anyParallel = Stream.of(1, 2, 3, 4).parallel().findAny();
System.out.println(anyParallel.orElse(-1));

Optional<Integer> any = Stream.of(1, 2, 3, 4).findAny();
System.out.println(any.orElse(-1));

3.3.5findFirst

findFirst方法用於獲取含有Stream中的第一個元素的Optional,如果Stream爲空,則返回一個空的Optional。若Stream並未排序,可能返回含有Stream中任意元素的Optional。

Optional<Integer> findFirst = Stream.of(1, 2, 3, 4).findFirst();
System.out.println(findFirst.orElse(-1));

3.3.6limit

limit方法將截取原Stream,截取後Stream的最大長度不能超過指定值N。如果原Stream的元素個數大於N,將截取原Stream的前N個元素;如果原Stream的元素個數小於或等於N,將截取原Stream中的所有元素。

Stream.of(1, 2, 3, 4, 5).limit(2L).forEach(System.out::println);
// 1
// 2

本文參考:
Java 8 中的 Streams API 詳解
Java 8系列之Stream的基本語法詳解
簡潔又快速地處理集合——Java8 Stream(上)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章