Java8 Stream流

一、Stream流的特性

  • Stream流不是一種數(shù)據(jù)結(jié)構(gòu),不保存數(shù)據(jù),它只是在原數(shù)據(jù)集上定義了一組操作。
  • 這些操作是惰性的,即每當(dāng)訪問到流中的一個(gè)元素,才會(huì)在此元素上執(zhí)行這一系列操作。
  • Stream不保存數(shù)據(jù),故每個(gè)Stream流只能使用一次。

關(guān)于應(yīng)用在Stream流上的操作,可以分成兩種:Intermediate(中間操作)和Terminal(終止操作)。中間操作的返回結(jié)果都是Stream,故可以多個(gè)中間操作疊加;終止操作用于返回我們最終需要的數(shù)據(jù),只能有一個(gè)終止操作。

二、Stream的操作分類

Stream的操作有Intermediate、Terminal和Short-circuiting:

  • Intermediate(中間操作):map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered
  • Terminal(終止操作):forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator
  • Short-circuiting(短路):anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit

三、惰性求值和及早求值方法

像filter這樣只描述Stream,最終不產(chǎn)生新集合的方法叫作惰性求值方法;而像count這樣最終會(huì)從Stream產(chǎn)生值的方法叫作及早求值方法。

long count = allArtists.stream()
    .filter(artist -> {
        System.out.println(artist.getName());
            return artist.isFrom("London");
        })
    .count();

如何判斷一個(gè)操作是惰性求值還是及早求值,其實(shí)很簡(jiǎn)單,只需要看其返回值即可:如果返回值是Stream,那么就是惰性求值;如果返回值不是Stream或者是void,那么就是及早求值。上面的示例中,只是包含兩步:一個(gè)惰性求值filter和一個(gè)及早求值count。
在一個(gè)Stream操作中,可以有多次惰性求值,但有且僅有一次及早求值。

四、對(duì)Stream的操作一般可以歸納為3個(gè)部分

1. 創(chuàng)建Stream

  • 靜態(tài)工廠方法
    of方法,其生成的Stream是有限長(zhǎng)度的,Stream的長(zhǎng)度為其內(nèi)的元素個(gè)數(shù)。
- of(T... values):返回含有多個(gè)T元素的Stream
- of(T t):返回含有一個(gè)T元素的Stream
示例:
Stream<Integer> integerStream = Stream.of(1, 2, 3);
Stream<String> stringStream = Stream.of("A");
  • generator方法,返回一個(gè)無限長(zhǎng)度的Stream,其元素由Supplier接口的提供。
- generate(Supplier<T> s):返回一個(gè)無限長(zhǎng)度的Stream
示例:
Stream<Double> generateA = Stream.generate(new Supplier<Double>() {
    @Override
    public Double get() {
        return java.lang.Math.random();
    }
});

Stream<Double> generateB = Stream.generate(()-> java.lang.Math.random());
Stream<Double> generateC = Stream.generate(java.lang.Math::random);

以上三種形式達(dá)到的效果是一樣的,只不過是下面的兩個(gè)采用了Lambda表達(dá)式,簡(jiǎn)化了代碼,其實(shí)際效果就是返回一個(gè)隨機(jī)值。一般無限長(zhǎng)度的Stream會(huì)與filter、limit等配合使用,否則Stream會(huì)無限制的執(zhí)行下去。

  • iterate方法,其返回的也是一個(gè)無限長(zhǎng)度的Stream,與generate方法不同的是,其是通過函數(shù)f迭代對(duì)給指定的元素種子而產(chǎn)生無限連續(xù)有序Stream,其中包含的元素可以認(rèn)為是:seed,f(seed),f(f(seed))無限循環(huán)。
- iterate(T seed, UnaryOperator<T> f)
示例:
Stream.iterate(1, item -> item + 1)
        .limit(10)
        .forEach(System.out::println); 
        // 打印結(jié)果:1,2,3,4,5,6,7,8,9,10

上面示例,種子為1,也可認(rèn)為該Stream的第一個(gè)元素,通過f函數(shù)來產(chǎn)生第二個(gè)元素。接著,第二個(gè)元素,作為產(chǎn)生第三個(gè)元素的種子,從而產(chǎn)生了第三個(gè)元素,以此類推下去。需要主要的是,該Stream也是無限長(zhǎng)度的,應(yīng)該使用filter、limit等來截取Stream,否則會(huì)一直循環(huán)下去。

  • empty方法返回一個(gè)空的順序Stream,該Stream里面不包含元素項(xiàng)。

  • Collection接口和數(shù)組的默認(rèn)方法
    在Collection接口中,定義了一個(gè)默認(rèn)方法stream(),用來生成一個(gè)Stream。

    public interface Collection<E> extends Iterable<E> {
        ***
        default Stream<E> stream() {
            return StreamSupport.stream(spliterator(), false);
        }
        ***
    }

在Arrays類,封裝了一些列的Stream方法,不僅針對(duì)于任何類型的元素采用了泛型,更對(duì)于基本類型作了相應(yīng)的封裝,以便提升Stream的處理效率。

public class Arrays {
    ***
    public static <T> Stream<T> stream(T[] array) {
        return stream(array, 0, array.length);
    }

   public static LongStream stream(long[] array) {
        return stream(array, 0, array.length);
    }
    ***
}
示例:
int ids[] = new int[]{1, 2, 3, 4};
Arrays.stream(ids)
        .forEach(System.out::println);
  • 其他
    Random.ints()
    BitSet.stream()
    Pattern.splitAsStream(java.lang.CharSequence)
    JarFile.stream()

2. Intermediate:通過一系列中間(Intermediate)方法,對(duì)數(shù)據(jù)集進(jìn)行過濾、檢索等數(shù)據(jù)集的再次處理,實(shí)際上是將源Stream轉(zhuǎn)換為一個(gè)新的Stream,以達(dá)到需求效果。

  • concat
    concat方法將兩個(gè)Stream連接在一起,合成一個(gè)Stream。若兩個(gè)輸入的Stream都是排序的,則新Stream也是排序的;若輸入的Stream中任何一個(gè)是并行的,則新的Stream也是并行的;若關(guān)閉新的Stream時(shí),原兩個(gè)輸入的Stream都將執(zhí)行關(guān)閉處理。
Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5))
       .forEach(integer -> System.out.print(integer + "  "));
// 打印結(jié)果:  1  2  3  4  5  
  • distinct
    distinct方法以達(dá)到去除掉原Stream中重復(fù)的元素,生成的新Stream中沒有重復(fù)的元素。
Stream.of(1,2,3,1,2,3)
        .distinct()
        .forEach(System.out::println); 
// 打印結(jié)果:1,2,3
  • filter
    filter方法對(duì)原Stream按照指定條件過濾,在新建的Stream中,只包含滿足條件的元素,將不滿足條件的元素過濾掉。
Stream.of(1, 2, 3, 4, 5)
        .filter(item -> item > 3)
        .forEach(System.out::println);
// 打印結(jié)果:4,5

\color{red}{filter傳入的Lambda表達(dá)式必須是Predicate實(shí)例,參數(shù)可以為任意類型,而其返回值必須是boolean類型。}

  • map
    map方法將對(duì)于Stream中包含的元素使用給定的轉(zhuǎn)換函數(shù)進(jìn)行轉(zhuǎn)換操作,新生成的Stream只包含轉(zhuǎn)換生成的元素。為了提高處理效率,官方已封裝好了,三種變形:mapToDouble,mapToInt,mapToLong。其實(shí)很好理解,如果想將原Stream中的數(shù)據(jù)類型,轉(zhuǎn)換為double,int或者是long是可以調(diào)用相對(duì)應(yīng)的方法。
Stream.of("a", "b", "hello")
        .map(item-> item.toUpperCase())     // 這里與flatMap不同
        .forEach(System.out::println);
        // 打印結(jié)果
        // A, B, HELLO

\color{red}{map傳入的Lambda表達(dá)式必須是Function實(shí)例,參數(shù)可以為任意類型,而其返回值也是任性類型,javac會(huì)根據(jù)實(shí)際情景自行推斷。}

  • flatMap
    flatMap方法與map方法類似,都是將原Stream中的每一個(gè)元素通過轉(zhuǎn)換函數(shù)轉(zhuǎn)換不同的是,該換轉(zhuǎn)函數(shù)的對(duì)象是一個(gè)Stream,也不會(huì)再創(chuàng)建一個(gè)新的Stream,而是將原Stream的元素取代為轉(zhuǎn)換的Stream。如果轉(zhuǎn)換函數(shù)生產(chǎn)的Stream為null,應(yīng)由空Stream取代。flatMap有三個(gè)對(duì)于原始類型的變種方法,分別是:flatMapToInt,flatMapToLong和flatMapToDouble。
Stream.of(1, 2, 3)
    .flatMap(integer -> Stream.of(integer * 10))   //這里與map不同
    .forEach(System.out::println);
    // 打印結(jié)果
    // 10,20,30

傳給flatMap中的表達(dá)式接受了一個(gè)Integer類型的參數(shù),通過轉(zhuǎn)換函數(shù),將原元素乘以10后,生成一個(gè)只有該元素的流,該流取代原流中的元素。
\color{red}{flatMap傳入的Lambda表達(dá)式必須是Function實(shí)例,參數(shù)可以為任意類型,而其返回值類型必須是一個(gè)Stream。}

  • peek
    peek方法生成一個(gè)包含原Stream的所有元素的新Stream,同時(shí)會(huì)提供一個(gè)消費(fèi)函數(shù)(Consumer實(shí)例),新Stream每個(gè)元素被消費(fèi)的時(shí)候都會(huì)執(zhí)行給定的消費(fèi)函數(shù),并且消費(fèi)函數(shù)優(yōu)先執(zhí)行
Stream.of(1, 2, 3, 4, 5)
        .peek(integer -> System.out.println("accept:" + integer))
        .forEach(System.out::println);
// 打印結(jié)果
// accept:1
//  1
//  accept:2
//  2
//  accept:3
//  3
//  accept:4
//  4
//  accept:5
//  5
  • skip
    skip方法將過濾掉原Stream中的前N個(gè)元素,返回剩下的元素所組成的新Stream。如果原Stream的元素個(gè)數(shù)大于N,將返回原Stream的后(原Stream長(zhǎng)度-N)個(gè)元素所組成的新Stream;如果原Stream的元素個(gè)數(shù)小于或等于N,將返回一個(gè)空Stream。
Stream.of(1, 2, 3,4,5)
.skip(2)
.forEach(System.out::println);
// 打印結(jié)果
// 3,4,5
  • sorted
    sorted方法將對(duì)原Stream進(jìn)行排序,返回一個(gè)有序列的新Stream。sorterd有兩種變體sorted(),sorted(Comparator),前者將默認(rèn)使用Object.equals(Object)進(jìn)行排序,而后者接受一個(gè)自定義排序規(guī)則函數(shù)(Comparator),可按照意愿排序。
Stream.of(5, 4, 3, 2, 1)
        .sorted()
        .forEach(System.out::println);
        // 打印結(jié)果
        // 1,2,3,4,5

Stream.of(1, 2, 3, 4, 5)
        .sorted((o1, o2) -> o2 - o1)    //逆序
        .forEach(System.out::println);
        // 打印結(jié)果
        // 5, 4, 3, 2, 1

3. Terminal通過最終(terminal)方法完成對(duì)數(shù)據(jù)集中元素的處理。

long count = Stream.of(1, 2, 3, 4, 5)
        .count();
System.out.println("count:" + count);// 打印結(jié)果:count:5
  • forEach
    forEach方法前面已經(jīng)用了好多次,其用于遍歷Stream中的所元素,避免了使用for循環(huán),讓代碼更簡(jiǎn)潔,邏輯更清晰。
Stream.of(5, 4, 3, 2, 1)
    .sorted()
    .forEach(System.out::println);
    // 打印結(jié)果
    // 1,2,3,4,5
  • forEachOrdered
    forEachOrdered方法與forEach類似,都是遍歷Stream中的所有元素,不同的是,如果該Stream預(yù)先設(shè)定了順序,會(huì)按照預(yù)先設(shè)定的順序執(zhí)行(Stream是無序的),默認(rèn)為元素插入的順序。
Stream.of(5,2,1,4,3)
        .forEachOrdered(integer -> {
            System.out.println("integer:"+integer);
        }); 
        // 打印結(jié)果
        // integer:5
        // integer:2
        // integer:1
        // integer:4
        // integer:3
  • max
    max方法根據(jù)指定的Comparator,返回一個(gè)Optional,該Optional中的value值就是Stream中最大的元素。

原Stream根據(jù)比較器Comparator,進(jìn)行排序(升序或者是降序),所謂的最大值就是從新進(jìn)行排序的,max就是取重新排序后的最后一個(gè)值,而min取排序后的第一個(gè)值。

Optional<Integer> max = Stream.of(1, 2, 3, 4, 5)
        .max((o1, o2) -> o2 - o1);
System.out.println("max:" + max.get());// 打印結(jié)果:max:1
  • min
    min方法根據(jù)指定的Comparator,返回一個(gè)Optional,該Optional中的value值就是Stream中最小的元素。
Optional<Integer> max = Stream.of(1, 2, 3, 4, 5)
        .max((o1, o2) -> o1 - o2);
System.out.println("max:" + max.get());// 打印結(jié)果:min:5

五、Short-circuiting操作

  • allMatch
    allMatch操作用于判斷Stream中的元素是否全部滿足指定條件。如果全部滿足條件返回true,否則返回false。
boolean allMatch = Stream.of(1, 2, 3, 4)
    .allMatch(integer -> integer > 0);
System.out.println("allMatch: " + allMatch); // 打印結(jié)果:allMatch: true 
  • findAny
    findAny操作用于獲取含有Stream中的某個(gè)元素的Optional,如果Stream為空,則返回一個(gè)空的Optional。由于此操作的行動(dòng)是不確定的,其會(huì)自由的選擇Stream中的任何元素。在并行操作中,在同一個(gè)Stream中多次調(diào)用,可能會(huì)不同的結(jié)果。在串行調(diào)用時(shí),Debug了幾次,發(fā)現(xiàn)每次都是獲取的第一個(gè)元素,個(gè)人感覺在串行調(diào)用時(shí),應(yīng)該默認(rèn)的是獲取第一個(gè)元素。
Optional<Integer> any = Stream.of(1, 2, 3, 4).findAny();
 System.out.println(any.get());
//輸出結(jié)果:1
  • findFirst
    findFirst操作用于獲取含有Stream中的第一個(gè)元素的Optional,如果Stream為空,則返回一個(gè)空的Optional。若Stream并未排序,可能返回含有Stream中任意元素的Optional。
Optional<Integer> any = Stream.of(1, 2, 3, 4).findFirst();
  • limit
    limit方法將截取原Stream,截取后Stream的最大長(zhǎng)度不能超過指定值N。如果原Stream的元素個(gè)數(shù)大于N,將截取原Stream的前N個(gè)元素;如果原Stream的元素個(gè)數(shù)小于或等于N,將截取原Stream中的所有元素。
Stream.of(1, 2, 3,4,5)
        .limit(2)
        .forEach(System.out::println);
        // 打印結(jié)果
        // 1,2

傳入limit的值為2,也就是說被截取后的Stream的最大長(zhǎng)度為2,又由于原Stream中有5個(gè)元素,所以將截取原Stream中的前2個(gè)元素,生成一個(gè)新的Stream。

  • noneMatch
    noneMatch方法將判斷Stream中的所有元素是否滿足指定的條件,如果所有元素都不滿足條件,返回true;否則,返回false。
    boolean noneMatch = Stream.of(1, 2, 3, 4, 5)
        .noneMatch(integer -> integer > 10);
    System.out.println("noneMatch:" + noneMatch); // 打印結(jié)果 noneMatch:true

    boolean noneMatch_ = Stream.of(1, 2, 3, 4, 5)
            .noneMatch(integer -> integer < 3);
    System.out.println("noneMatch_:" + noneMatch_); // 打印結(jié)果 noneMatch_:false
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Jav8中,在核心類庫(kù)中引入了新的概念,流(Stream)。流使得程序媛們得以站在更高的抽象層次上對(duì)集合進(jìn)行操作。...
    仁昌居士閱讀 4,119評(píng)論 0 6
  • 首先,Stream流有一些特性:1 .Stream流不是一種數(shù)據(jù)結(jié)構(gòu),不保存數(shù)據(jù),它只是在原數(shù)據(jù)集上定義了一組操作...
    pretty_rain閱讀 790評(píng)論 0 3
  • 記一次糟心的使用: 前奏: 首先stream流分為三個(gè)階段: 1.創(chuàng)建 2.中間操作 :返回的還是流 ...
    砂糖z閱讀 444評(píng)論 0 2
  • 引言 Stream意為流,是Lambda編程中的一個(gè)重要角色。Stream類主要用于對(duì)收集類、數(shù)組、文件的迭代,以...
    斯特的簡(jiǎn)書閱讀 565評(píng)論 0 0
  • 我要去 去一個(gè)夢(mèng)想的地方 那里有我心靈的牧場(chǎng) 清風(fēng)飄蕩,愛的向往 溫柔的穿過我的心房 我要去阿 去看看無邊的佛光 ...
    月牙兒彎閱讀 296評(píng)論 2 2

友情鏈接更多精彩內(nèi)容