摘要:中的與表達式可以說是相伴相生的,通過我們可以更好的更為流暢更為語義化的操作集合。流是一種惰性操作,所有對源數據的計算只在終止操作被初始化的時候才會執行。然后傳入表達式將每個元素轉換大寫,通過方法將結果收集到中。
上一篇系統學了方法引用的幾種類型及應用場景,本篇開始我們正式學習Stream。
Java8中的Stream與lambda表達式可以說是相伴相生的,通過Stream我們可以更好的更為流暢更為語義化的操作集合。Stream api都位于java.util.stream包中。其中就包含了最核心的Stream接口,一個Stream實例可以串行或者并行操作一組元素序列,官方文檔中給出了一個示例
*{@code * int sum = widgets.stream()//創建一個流 * .filter(w -> w.getColor() == RED)//取出顏色是紅色的元素 * .mapToInt(w -> w.getWeight())//返回每個紅色元素的重量 * .sum();//重量求和 * }
Java8中,所有的流操作會被組合到一個 stream pipeline中,這點類似linux中的pipeline概念,將多個簡單操作連接在一起組成一個功能強大的操作。一個 stream pileline首先會有一個數據源,這個數據源可能是數組、集合、生成器函數或是IO通道,流操作過程中并不會修改源中的數據;然后還有零個或多個中間操作,每個中間操作會將接收到的流轉換成另一個流(比如filter);最后還有一個終止操作,會生成一個最終結果(比如sum)。流是一種惰性操作,所有對源數據的計算只在終止操作被初始化的時候才會執行。
總結一下流操作由3部分組成
1.源
2.零個或多個中間操作
3.終止操作 (到這一步才會執行整個stream pipeline計算)
創建流的幾種方式
//第一種 通過Stream接口的of靜態方法創建一個流 Streamstream = Stream.of("hello", "world", "helloworld"); //第二種 通過Arrays類的stream方法,實際上第一種of方法底層也是調用的Arrays.stream(values); String[] array = new String[]{"hello","world","helloworld"}; Stream stream3 = Arrays.stream(array); //第三種 通過集合的stream方法,該方法是Collection接口的默認方法,所有集合都繼承了該方法 Stream stream2 = Arrays.asList("hello","world","helloworld").stream();
接下來我們看一個簡單的需求:將流中字符全部轉成大寫返回一個新的集合
Listlist = Arrays.asList("hello", "world", "helloworld"); List collect = list.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());
這里我們使用了Stream的map方法,map方法接收一個Function函數式接口實例,這里的map和Hadoop中的map概念完全一致,對每個元素進行映射處理。然后傳入lambda表達式將每個元素轉換大寫,通過collect方法將結果收集到ArrayList中。
Stream map(Function super T, ? extends R> mapper);//map函數定義
那如果我們想把結果放到Set中或者替他的集合容器,也可以這樣
list.stream().map(s -> s.toUpperCase()).collect(Collectors.toSet());//放到Set中
或者更為通用的
list.stream().map(s -> s.toUpperCase()).collect(Collectors.toCollection(TreeSet::new));//自定義容器類型
我們可以自己制定結果容器的類型Collectors的toCollection接受一個Supplier函數式接口類型參數,可以直接使用構造方法引用的方式。
Stream中除了map方法對元素進行映射外,還有一個flatMap方法
Stream flatMap(Function super T, ? extends Stream extends R>> mapper);
flatMap從方法命名上可以解釋為扁平的map
map方法是將一個容器里的元素映射到另一個容器中。
flatMap方法,可以將多個容器的元素全部映射到一個容器中,即為扁平的map。
看一個求每個元素平方的例子
Stream> listStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6)); List
collect1 = listStream.flatMap(theList -> theList.stream()). map(integer -> integer * integer).collect(Collectors.toList());
首先我們創建了一個Stream對象,Stream中的每個元素都是容器List
然后介紹一個用于獲取統計信息的方法
//同時獲取最大 最小 平均值等信息 Listlist1 = 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. * * @paramthe type of stream elements * @param s the {@code Supplier} of generated elements * @return a new infinite sequential unordered {@code Stream} */ public static Stream generate(Supplier 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. * *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
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 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); }
iterate方法有兩個參數,第一個是seed也可以稱作種子,第二個是一個UnaryOperator,UnaryOperator實際上是Function的一個子接口,和Funciton區別就是參數和返回類型都是同一種類型
@FunctionalInterface public interface UnaryOperatorextends Function { }
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()一直等待那程序會一直執行不斷生成數據,所以需要先限制結果集再去進行去重操作就可以了。
下一篇
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/70730.html
摘要:個人前端文章整理從最開始萌生寫文章的想法,到著手開始寫,再到現在已經一年的時間了,由于工作比較忙,更新緩慢,后面還是會繼更新,現將已經寫好的文章整理一個目錄,方便更多的小伙伴去學習。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 個人前端文章整理 從最開始萌生寫文章的想法,到著手...
摘要:異步剪貼板操作過去的數年中,各瀏覽器基本上都在使用來進行剪貼板交互。而提供了新的,則為我們提供了另一種異步式的剪貼板操作方式,本文即是對該機制與接口規范的詳細介紹。 showImg(https://segmentfault.com/img/remote/1460000013854167); 前端每周清單第 55 期: MobX 4 特性概覽,iOS Hacks 分享, 分布式事務詳解 ...
摘要:基礎問題的的性能及原理之區別詳解備忘筆記深入理解流水線抽象關鍵字修飾符知識點總結必看篇中的關鍵字解析回調機制解讀抽象類與三大特征時間和時間戳的相互轉換為什么要使用內部類對象鎖和類鎖的區別,,優缺點及比較提高篇八詳解內部類單例模式和 Java基礎問題 String的+的性能及原理 java之yield(),sleep(),wait()區別詳解-備忘筆記 深入理解Java Stream流水...
摘要:基礎問題的的性能及原理之區別詳解備忘筆記深入理解流水線抽象關鍵字修飾符知識點總結必看篇中的關鍵字解析回調機制解讀抽象類與三大特征時間和時間戳的相互轉換為什么要使用內部類對象鎖和類鎖的區別,,優缺點及比較提高篇八詳解內部類單例模式和 Java基礎問題 String的+的性能及原理 java之yield(),sleep(),wait()區別詳解-備忘筆記 深入理解Java Stream流水...
閱讀 838·2021-09-22 15:18
閱讀 1191·2021-09-09 09:33
閱讀 2762·2019-08-30 10:56
閱讀 1197·2019-08-29 16:30
閱讀 1495·2019-08-29 13:02
閱讀 1465·2019-08-26 13:55
閱讀 1650·2019-08-26 13:41
閱讀 1948·2019-08-26 11:56