Java 8 中的 Stream 是對集合(Collection)對象功能的加強,它專一於對集合對象進行各類很是便利、高效的聚合操做(aggregate operation),或者大批量數據操做 (bulk data operation)。html
Stream表明數據流,流中的數據元素的數量多是有限的,也多是無限的。java
Stream和其它集合類的區別在於:其它集合類主要關注與有限數量的數據的訪問和有效管理(增刪改),而Stream並無提供訪問和管理元素的方式,而是經過聲明數據源的方式,利用可計算的操做在數據源上執行,固然BaseStream.iterator()
和 BaseStream.spliterator()
操做提供了遍歷元素的方法。編程
Java Stream提供了提供了串行和並行兩種類型的流,保持一致的接口,提供函數式編程方式,以管道方式提供中間操做和最終執行操做,爲Java語言的集合提供了現代語言提供的相似的高階函數操做,簡化和提升了Java集合的功能。api
Stream接口還包含幾個基本類型的子接口如IntStream, LongStream 和 DoubleStream。數組
關於流和其它集合具體的區別,能夠參照下面的列表:多線程
filter
不會將數據源中的數據刪除。limit(n)
或 findFirst()
,這些操做但是實現"短路"(Short-circuiting),訪問到有限的元素後就能夠返回。流的操做是以管道的方式串起來的。流管道包含一個數據源,接着包含零到N箇中間操做,最後以一個終點操做結束。oracle
全部的流操做均可以串行執行或者並行執行。
除非顯示地建立並行流,不然Java庫中建立的都是串行流。 Collection.stream()
爲集合建立串行流而Collection.parallelStream()
爲集合建立並行流。IntStream.range(int, int)
建立的是串行流。經過parallel()
方法能夠將串行流轉換成並行流,sequential()
方法將流轉換成串行流。app
除非方法的Javadoc中指明瞭方法在並行執行的時候結果是不肯定(好比findAny、forEach),不然串行和並行執行的結果應該是同樣的。dom
能夠經過多種方式建立流:函數式編程
一、經過集合的stream()
方法或者parallelStream()
,好比Arrays.asList(1,2,3).stream()
。
二、經過Arrays.stream(Object[])
方法, 好比Arrays.stream(new int[]{1,2,3})
。
三、使用流的靜態方法,好比Stream.of(Object[])
, IntStream.range(int, int)
或者 Stream.iterate(Object, UnaryOperator)
,如Stream.iterate(0, n -> n * 2)
,或者generate(Supplier<T> s)
如Stream.generate(Math::random)
。
四、BufferedReader.lines()
從文件中得到行的流。
五、Files
類的操做路徑的方法,如list
、find
、walk
等。
六、隨機數流Random.ints()
。
七、其它一些類提供了建立流的方法,如BitSet.stream()
, Pattern.splitAsStream(java.lang.CharSequence)
, 和 JarFile.stream()
。
八、更底層的使用StreamSupport
,它提供了將Spliterator
轉換成流的方法。
中間操做會返回一個新的流,而且操做是延遲執行的(lazy),它不會修改原始的數據源,並且是由在終點操做開始的時候才真正開始執行。
distinct
保證輸出的流中包含惟一的元素,它是經過Object.equals(Object)
來檢查是否包含相同的元素。
下面的例子則使用 distinct 來找出不重複的單詞。
List<String> l = Stream.of("a","b","c","b").distinct().collect(Collectors.toList()); System.out.println(l); 輸出結果:[a, b, c]
filter
返回的流中只包含知足斷言(predicate)的數據。
下面的代碼返回流中的偶數集合。
List<Integer> l = IntStream.range(1,10).filter( i -> i % 2 == 0).boxed().collect(Collectors.toList()); System.out.println(l); 輸出結果:[2, 4, 6, 8]
map
方法將流中的元素映射成另外的值,新的值類型能夠和原來的元素的類型不一樣。
做用就是把 input Stream 的每個元素,映射成 output Stream 的另一個元素。
下面的代碼中將字符元素映射成它的哈希碼(ASCII值)。
List<Integer> l = Stream.of('a','b','c').map( c -> c.hashCode()).collect(Collectors.toList()); System.out.println(l); 輸出結果:[97, 98, 99]
從上面例子能夠看出,map 生成的是個 1:1 映射,每一個輸入元素,都按照規則轉換成爲另一個元素。還有一些場景,是一對多映射關係的,這時須要 flatMap。
Stream<List<Integer>> inputStream = Stream.of( Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6) ); List<Integer> outputList = inputStream.flatMap((childList) -> childList.stream()) .collect(Collectors.toList()); 輸出結果:[1,2,3,4,5,6]
將最底層元素抽出來放到一塊兒,最終 output 的新 Stream 裏面。
limit 返回 Stream 的前面 n 個元素;skip 則是扔掉前 n 個元素
List<Integer> l = IntStream.range(1,100).limit(5).skip(1) .boxed()//IntStream轉換成Stream<Integer> .collect(Collectors.toList()); System.out.println(l); 輸出結果:[2, 3, 4, 5]
peek 對每一個元素執行操做並返回一個新的 Stream
Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toUpperCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList()); 輸出結果: Filtered value: three Mapped value: THREE Filtered value: four Mapped value: FOUR
forEach 不能修改本身包含的本地變量值,也不能用 break/return 之類的關鍵字提早結束循環。
sorted()
將流中的元素按照天然排序方式進行排序,若是元素沒有實現Comparable
,則終點操做執行時會拋出java.lang.ClassCastException
異常。
sorted(Comparator<? super T> comparator)
能夠指定排序的方式。
對於有序流,排序是穩定的。對於非有序流,不保證排序穩定。
降序排列: List<String> li = Stream.of("c","d", "b", "a").sorted((s1,s2) ->s2.compareTo(s1)) .collect(Collectors.toList()); System.out.println(li); 輸出結果:[d, c, b, a] 升序排序: List<String> li = Stream.of("c","d", "b", "a").sorted((s1,s2) ->s1.compareTo(s2)) .collect(Collectors.toList()); System.out.println(li); 輸出結果:[a, b, c, d]
這一組方法用來檢查流中的元素是否知足斷言。
allMatch
只有在全部的元素都知足斷言時才返回true,不然flase,流爲空時老是返回true
anyMatch
只有在任意一個元素知足斷言時就返回true,不然flase,
noneMatch
只有在全部的元素都不知足斷言時才返回true,不然flase,
System.out.println(Stream.of(1,2,3,4,5).allMatch( i -> i > 0)); 輸出結果:true System.out.println(Stream.of(1,2,3,4,5).anyMatch( i -> i > 0)); 輸出結果:true System.out.println(Stream.of(1,2,3,4,5).noneMatch( i -> i > 0)); 輸出結果:false System.out.println(Stream.<Integer>empty().allMatch( i -> i > 0)); 輸出結果:true System.out.println(Stream.<Integer>empty().anyMatch( i -> i > 0)); 輸出結果:false System.out.println(Stream.<Integer>empty().noneMatch( i -> i > 0)); 輸出結果:true
count
方法返回流中的元素的數量
System.out.println(Stream.of("c","d", "b", "a").count()); 輸出結果:4
使用一個collector執行mutable reduction
操做。輔助類Collectors
提供了不少的collector,java.util.stream.Collectors 類的主要做用就是輔助進行各種有用的 reduction 操做,例如轉變輸出爲 Collection,把 Stream 元素進行歸組。
System.out.println(Stream.of("c","d", "b", "a").collect(Collectors.toList())); 輸出結果:[c, d, b, a]
findAny()
返回任意一個元素,若是流爲空,返回空的Optional,對於並行流來講,它只須要返回任意一個元素便可,因此性能可能要好於findFirst()
,可是有可能屢次執行的時候返回的結果不同。
findFirst()
返回第一個元素,若是流爲空,返回空的Optional。
String l = Stream.of("cd","dc", "acb", "ade").filter(e -> e.contains("a")).findFirst().get(); System.out.println(l); 輸出結果:acb
forEach
遍歷流的每個元素,執行指定的action。它是一個終點操做,和peek
方法不一樣。這個方法不擔保按照流的encounter order
順序執行,若是對於有序流按照它的encounter order
順序執行,你可使用forEachOrdered
方法。
Stream.of(1,2,3).forEach(System.out::println); 輸出結果: 1 2 3
max
返回流中的最大值,
min
返回流中的最小值。
System.out.println(IntStream.range(1,10).max().getAsInt()); System.out.println(IntStream.range(1,10).min().getAsInt()); 輸出結果: 9 1
這個方法的主要做用是把 Stream 元素組合起來。它提供一個起始值(種子),而後依照運算規則(BinaryOperator),和前面 Stream 的第一個、第二個、第 n 個元素組合。從這個意義上說,字符串拼接、數值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就至關於
Integer sum = integers.reduce(0, (a, b) -> a+b); 或
Integer sum = integers.reduce(0, Integer::sum);
也有沒有起始值的狀況,這時會把 Stream 的前面兩個元素組合起來,返回的是 Optional。
Integer total = Stream.of(2,1,3,4,5).reduce( (x, y) -> x +y).get(); System.out.println(total); Integer total2 = Stream.of(2,1,3,4,5).reduce(2, (x, y) -> x +y); System.out.println(total2); 輸出結果: 15 17
將流中的元素放入到一個數組中。
像上面所說的,流操做能夠是串行的,也能夠是並行的。串行操做經過單線程執行,而並行操做則經過多線程執行。
下面的例子就演示瞭如何使用並行流進行操做來提升運行效率,代碼很是簡單。
首先咱們建立一個大的list,裏面的元素都是惟一的:
int max = 1000000; List<String> values = new ArrayList<>(max); for (int i = 0; i < max; i++) { UUID uuid = UUID.randomUUID(); values.add(uuid.toString()); }
如今,咱們測量一下對這個集合進行排序所使用的時間。
long t0 = System.nanoTime(); long count = values.stream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("sequential sort took: %d ms", millis)); // sequential sort took: 899 ms
long t0 = System.nanoTime(); long count = values.parallelStream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("parallel sort took: %d ms", millis)); // parallel sort took: 472 ms
如你所見,全部的代碼段幾乎都相同,惟一的不一樣就是把stream()
改爲了parallelStream()
, 結果並行排序快了50%。