當處理集合時,通常會迭代所有元素并對其中的每一個進行處理,這樣的處理使它很難被并行運算。在java8中的StreamAPI可以實現相同功能同時支持并行運算。一個Stream表面上看與一個集合很類似,允許你改變和獲取數據。但是實際上它與集合有很大區別:
- Stream自己不會存儲元素。元素可以被存儲在底層的集合中,或者根據需要產生出來。
- Stream操作符不會改變源對象。相反,它們會返回一個持有結果的新Stream。
- Stream操作符可能是延遲執行的。這意味著它們會等到需要結果的時候才執行。
Stream遵循“做什么,而不是怎么去做”的原則。使用步驟:
- 創建一個Stream。
- 在一個或多個步驟中,指定將初始Stream轉換為另一個Stream的中間操作。
- 使用一個終止操作來產生一個結果。該操作會強制它之前的延遲操作立即執行。在這之后,該Stream就不會再被使用了。
創建Stream
- 通過java8在Collection接口中新添加的stream方法,可以將任何集合轉化為一個Stream。
- Stream<String> word = Stream.of("early","more","day");
- 使用Arrays.stream(arrray, from, to)方法將數組的一部分轉化為Stream。
- Stream.empty(); 創建一個不含任何元素的Stream的靜態方法。
- Stream接口有兩個用來創建無限Stream的靜態方法。generate方法接受一個無參數的函數,因此可以創建一個含有常量值的Stream: Stream<String> echos = Stream.generate(() -> "Echo"); 創建一個含有隨機數字的Stream: Stream<Double> randoms = Stream.generate(Math::random); 創建一個形如0、1、2、3...的無限序列,可以使用iterate方法。它接受一個“種子(seed)”值和一個函數(從技術上講,是一個UnaryOperator<T>接口的對象)作為參數,并且會對之前的值重復應用該函數。如:Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE)); 序列中的第一個元素是種子BigInteger.ZERO; 第二個值是f(seed),或者1(一個大整數); 下一個元素是f(f(seed)), 或者2;以此類推。
filter、map和flatMap方法
流轉換是指從一個流中讀取數據,并將轉換后的數據寫入到另一個流中。將一個字符串流轉換到另一個只包含長單詞的流:
List<String> wordList = ...;
Stream<String> words = wordList.stream();
Stream<String> longWords = words.filter(w -> w.length() > 12);
我們經常需要對一個流中的值進行某種形式的轉換。這時可以考慮使用map方法,并傳遞給它一個執行轉換的函數。 可以通過:
Stream<String> lowercaseWords = words.map(String::toLoweCase);
將所有單詞轉換為小寫形式,這里使用了帶有一個方法引用的map方法。通常,用lambda表達式來代替方法表達式;
Stream<Character> firstChars = words.map(s -> s.charAt(0));
該方法產生的流會包含每個單詞的第一個字符。
使用map方法時,會對每個元素應用一個函數,并將返回的值收集到一個新的流中。但當函數返回值不是一個值而是一個包含多個值的流,此時要將多個流展開成成一個流就需要使用flatMap方法,而不是map方法。
提取子流和組合流
Stream.limit(n)會返回一個包含n個元素的新流(如果原始流的長度小于n,則會返回原始的流)。這個方法特別適用于剪裁指定長度的流。例如:
Stream<Double> randoms = Stream.generate(Math::random).limit(100);
Stream.skip(n)正好相反,它會丟棄前面的n個元素。同時還可以使用Stream類的靜態方法concat將兩個流連在一起。