【Java8新特性】面試官問我:Java8中建立Stream流有哪幾種方式?

冰河團隊發表於2020-05-26

寫在前面

先說點題外話:不少讀者工作幾年後,仍然在使用Java7之前版本的方法,對於Java8版本的新特性,甚至是Java7的新特性幾乎沒有接觸過。真心想對這些讀者說:你真的需要了解下Java8甚至以後版本的新特性了。

今天,一名讀者出去面試,面試官問他:說說Java8中建立Stream流有哪幾種方式?他竟然沒回答上來!!

Stream概述

Java8中有兩大最為重要的改變。第一個是 Lambda 表示式;另外一個則是 Stream API(java.util.stream.*)。

Stream 是 Java8 中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常複雜的查詢、過濾和對映資料等操作。使用Stream API 對集合資料進行操作,就類似於使用 SQL 執行的資料庫查詢。也可以使用 Stream API 來並行執行操作。簡而言之,Stream API 提供了一種高效且易於使用的處理資料的方式。

何為Stream?

流(Stream) 到底是什麼呢?

可以這麼理解流:流就是資料渠道,用於運算元據源(集合、陣列等)所生成的元素序列。

“集合講的是資料,流講的是計算! ”

注意:

①Stream 自己不會儲存元素。

②Stream 不會改變源物件。相反,他們會返回一個持有結果的新Stream。

③Stream 操作是延遲執行的。這意味著他們會等到需要結果的時候才執行。

Stream操作步驟

1.建立 Stream

一個資料來源(如: 集合、陣列), 獲取一個流。

2.中間操作

一箇中間操作鏈,對資料來源的資料進行處理。

3.終止操作(終端操作)

一個終止操作,執行中間操作鏈,併產生結果 。

在這裡插入圖片描述

如何建立Stream流?

這裡,建立測試類TestStreamAPI1,所有的操作都是在TestStreamAPI1類中完成的。

(1)通過Collection系列集合提供的stream()方法或者parallelStream()方法來建立Stream。

在Java8中,Collection 介面被擴充套件,提供了兩個獲取流的預設方法,如下所示。

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

其中,stream()方法返回一個順序流,parallelStream()方法返回一個並行流。

我們可以使用如下程式碼方式來建立順序流和並行流。

List<String> list = new ArrayList<>();
list.stream();
list.parallelStream();

(2)通過Arrays中的靜態方法stream()獲取陣列流。

Java8 中的 Arrays類的靜態方法 stream() 可以獲取陣列流 ,如下所示。

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

上述程式碼的的作用為:傳入一個泛型陣列,返回這個泛型的Stream流。

除此之外,在Arrays類中還提供了stream()方法的如下過載形式。

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

public static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive) {
    return StreamSupport.stream(spliterator(array, startInclusive, endExclusive), false);
}

public static IntStream stream(int[] array) {
    return stream(array, 0, array.length);
}

public static IntStream stream(int[] array, int startInclusive, int endExclusive) {
    return StreamSupport.intStream(spliterator(array, startInclusive, endExclusive), false);
}

public static LongStream stream(long[] array) {
    return stream(array, 0, array.length);
}

public static LongStream stream(long[] array, int startInclusive, int endExclusive) {
    return StreamSupport.longStream(spliterator(array, startInclusive, endExclusive), false);
}

public static DoubleStream stream(double[] array) {
    return stream(array, 0, array.length);
}

public static DoubleStream stream(double[] array, int startInclusive, int endExclusive) {
    return StreamSupport.doubleStream(spliterator(array, startInclusive, endExclusive), false);
}

基本上能夠滿足基本將基本型別的陣列轉化為Stream流的操作。

我們可以通過下面的程式碼示例來使用Arrays類的stream()方法來建立Stream流。

Integer[] nums = new Integer[]{1,2,3,4,5,6,7,8,9};
Stream<Integer> numStream = Arrays.stream(nums);

(3)通過Stream類的靜態方法of()獲取陣列流。

可以使用靜態方法 Stream.of(), 通過顯示值建立一個流。它可以接收任意數量的引數。

我們先來看看Stream的of()方法,如下所示。

public static<T> Stream<T> of(T t) {
    return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}
@SafeVarargs
@SuppressWarnings("varargs") 
public static<T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}

可以看到,在Stream類中,提供了兩個of()方法,一個只需要傳入一個泛型引數,一個需要傳入一個可變泛型引數。

我們可以使用下面的程式碼示例來使用of方法建立一個Stream流。

Stream<String> strStream = Stream.of("a", "b", "c");

(4)建立無限流

可以使用靜態方法 Stream.iterate() 和Stream.generate(), 建立無限流。

先來看看Stream類中iterate()方法和generate()方法的原始碼,如下所示。

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
    Objects.requireNonNull(f);
    final Iterator<T> iterator = new Iterator<T>() {
        @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);
}

public static<T> Stream<T> generate(Supplier<T> s) {
    Objects.requireNonNull(s);
    return StreamSupport.stream(
        new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}

通過原始碼可以看出,iterate()方法主要是使用“迭代”的方式生成無限流,而generate()方法主要是使用“生成”的方式生成無限流。我們可以使用下面的程式碼示例來使用這兩個方法生成Stream流。

  • 迭代
Stream<Integer> intStream = Stream.iterate(0, (x) -> x + 2);
intStream.forEach(System.out::println);

執行上述程式碼,會在終端一直輸出偶數,這種操作會一直持續下去。如果我們只需要輸出10個偶數,該如何操作呢?其實也很簡單,使用Stream物件的limit方法進行限制就可以了,如下所示。

Stream<Integer> intStream = Stream.iterate(0, (x) -> x + 2);
intStream.limit(10).forEach(System.out::println);
  • 生成
Stream.generate(() -> Math.random()).forEach(System.out::println);

上述程式碼同樣會一直輸出隨機數,如果我們只需要輸出5個隨機數,則只需要使用limit()方法進行限制即可。

Stream.generate(() -> Math.random()).limit(5).forEach(System.out::println);

(5)建立空流

在Stream類中提供了一個empty()方法,如下所示。

public static<T> Stream<T> empty() {
    return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false);
}

我們可以使用Stream類的empty()方法來建立一個空Stream流,如下所示。

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

寫在最後

如果覺得文章對你有點幫助,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習Java8新特性。

最後,附上Java8新特性核心知識圖,祝大家在學習Java8新特性時少走彎路。

相關文章