Java8-9-Stream介紹與操做方式詳解

上一篇系統學了方法引用的幾種類型及應用場景,本篇開始咱們正式學習Stream。
Java8中的Stream與lambda表達式能夠說是相伴相生的,經過Stream咱們能夠更好的更爲流暢更爲語義化的操做集合。Stream api都位於java.util.stream包中。其中就包含了最核心的Stream接口,一個Stream實例能夠串行或者並行操做一組元素序列,官方文檔中給出了一個示例java

* <pre>{@code
 *     int sum = widgets.stream()//建立一個流
 *                      .filter(w -> w.getColor() == RED)//取出顏色是紅色的元素
 *                      .mapToInt(w -> w.getWeight())//返回每一個紅色元素的重量
 *                      .sum();//重量求和
 * }</pre>

Java8中,全部的流操做會被組合到一個 stream pipeline中,這點相似linux中的pipeline概念,將多個簡單操做鏈接在一塊兒組成一個功能強大的操做。一個 stream pileline首先會有一個數據源,這個數據源多是數組、集合、生成器函數或是IO通道,流操做過程當中並不會修改源中的數據;而後還有零個或多箇中間操做,每一箇中間操做會將接收到的流轉換成另外一個流(好比filter);最後還有一個終止操做,會生成一個最終結果(好比sum)。流是一種惰性操做,全部對源數據的計算只在終止操做被初始化的時候纔會執行。linux

總結一下流操做由3部分組成
1.源
2.零個或多箇中間操做
3.終止操做 (到這一步纔會執行整個stream pipeline計算)segmentfault

建立流的幾種方式api

//第一種 經過Stream接口的of靜態方法建立一個流
Stream<String> stream = Stream.of("hello", "world", "helloworld");
//第二種 經過Arrays類的stream方法,實際上第一種of方法底層也是調用的Arrays.stream(values);
String[] array = new String[]{"hello","world","helloworld"};
Stream<String> stream3 = Arrays.stream(array);
//第三種 經過集合的stream方法,該方法是Collection接口的默認方法,全部集合都繼承了該方法
Stream<String> stream2 = Arrays.asList("hello","world","helloworld").stream();

接下來咱們看一個簡單的需求:將流中字符所有轉成大寫返回一個新的集合數組

List<String> list = Arrays.asList("hello", "world", "helloworld");
List<String> collect = list.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());

這裏咱們使用了Stream的map方法,map方法接收一個Function函數式接口實例,這裏的map和Hadoop中的map概念徹底一致,對每一個元素進行映射處理。而後傳入lambda表達式將每一個元素轉換大寫,經過collect方法將結果收集到ArrayList中。app

<R> Stream<R> map(Function<? super T, ? extends R> mapper);//map函數定義

那若是咱們想把結果放到Set中或者替他的集合容器,也能夠這樣dom

list.stream().map(s -> s.toUpperCase()).collect(Collectors.toSet());//放到Set中

或者更爲通用的ide

list.stream().map(s -> s.toUpperCase()).collect(Collectors.toCollection(TreeSet::new));//自定義容器類型

咱們能夠本身制定結果容器的類型Collectors的toCollection接受一個Supplier函數式接口類型參數,能夠直接使用構造方法引用的方式。函數

Stream中除了map方法對元素進行映射外,還有一個flatMap方法oop

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

flatMap從方法命名上能夠解釋爲扁平的map

圖片描述
map方法是將一個容器裏的元素映射到另外一個容器中。
圖片描述
flatMap方法,能夠將多個容器的元素所有映射到一個容器中,即爲扁平的map。
看一個求每一個元素平方的例子

Stream<List<Integer>> listStream =
                Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
List<Integer> collect1 = listStream.flatMap(theList -> theList.stream()).
                map(integer -> integer * integer).collect(Collectors.toList());

首先咱們建立了一個Stream對象,Stream中的每一個元素都是容器List<Integer>類型,並使用三個容器list初始化這個Stream對象,而後使用flatMap方法將每一個容器中的元素映射到一個容器中,這時flatMap接收的參數Funciton的泛型T就是List<Integer>類型,返回類型就是T對應的Stream。最後再對這個容器使用map方法求出買個元素的平方。

而後介紹一個用於獲取統計信息的方法

//同時獲取最大 最小 平均值等信息
List<Integer> list1 = Arrays.asList(1, 3, 5, 7, 9, 11);
IntSummaryStatistics statistics = list1.stream().filter(integer -> integer > 2).mapToInt(i -> i * 2).skip(2).limit(2).summaryStatistics();
System.out.println(statistics.getMax());//18
System.out.println(statistics.getMin());//14
System.out.println(statistics.getAverage());//16

將list1中的數據取出大於2的,每一個數進行平方計算,skip(2)忽略前兩個,limit(2)再取出前兩個,summaryStatistics對取出的這兩個數計算統計數據。mapToInt接收一個ToIntFunction類型,也就是接收一個參數返回值是int類型。

接下來看一下Stream中的一個靜態方法,generate方法

/**
 * Returns an infinite sequential unordered stream where each element is
 * generated by the provided {@code Supplier}.  This is suitable for
 * generating constant streams, streams of random elements, etc.
 *
 * @param <T> the type of stream elements
 * @param s the {@code Supplier} of generated elements
 * @return a new infinite sequential unordered {@code Stream}
 */
public static<T> Stream<T> generate(Supplier<T> s) {
    Objects.requireNonNull(s);
    return StreamSupport.stream(
            new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}

generate接收一個Supplier,適合生成接二連三的流或者一個所有是隨機數的流

Stream.generate(UUID.randomUUID()::toString).findFirst().ifPresent(System.out::println);

使用UUID.randomUUID()::toString 方法引用的方式建立了Supplier,而後取出第一個元素,這裏的findFirst返回的是 Optional,由於流中有可能沒有元素,爲了不空指針,在使用前 ifPresent 進行是否存在的判斷。

最後再學習一下另外一個靜態方法,iterate

/**
 * Returns an infinite sequential ordered {@code Stream} produced by iterative
 * application of a function {@code f} to an initial element {@code seed},
 * producing a {@code Stream} consisting of {@code seed}, {@code f(seed)},
 * {@code f(f(seed))}, etc.
 *
 * <p>The first element (position {@code 0}) in the {@code Stream} will be
 * the provided {@code seed}.  For {@code n > 0}, the element at position
 * {@code n}, will be the result of applying the function {@code f} to the
 * element at position {@code n - 1}.
 *
 * @param <T> the type of stream elements
 * @param seed the initial element
 * @param f a function to be applied to to the previous element to produce
 *          a new element
 * @return a new sequential {@code Stream}
 */
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);
}

iterate方法有兩個參數,第一個是seed也能夠稱做種子,第二個是一個UnaryOperator,UnaryOperator其實是Function的一個子接口,和Funciton區別就是參數和返回類型都是同一種類型

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {

}

iterate方法第一次生成的元素是UnaryOperator對seed執行apply後的返回值,以後全部生成的元素都是UnaryOperator對上一個apply的返回值再執行apply,不斷循環。
f(f(f(f(f(f(n))))))......

//從1開始,每一個元素比前一個元素大2,最多生成10個元素
Stream.iterate(1,item -> item + 2).limit(10).forEach(System.out::println);

咱們在使用stream api時也要注意一些陷阱,好比下面這個例子

//Stream陷阱 distinct()會一直等待產生的結果去重,將distinct()和limit(6)調換位置,先限制結果集再去重就能夠了
IntStream.iterate(0,i -> (i + 1) % 2).distinct().limit(6).forEach(System.out::println);

若是distinct()一直等待那程序會一直執行不斷生成數據,因此須要先限制結果集再去進行去重操做就能夠了。

下一篇

相關文章
相關標籤/搜索