如何使用Stream?
聚合操做是Java 8針對集合類,使編程更爲便利的方式,能夠與Lambda表達式一塊兒使用,達到更加簡潔的目的。java
前面例子中,對聚合操做的使用能夠歸結爲3個部分:編程
1) 建立Stream:經過stream()方法,取得集合對象的數據集。
2) Intermediate:經過一系列中間(Intermediate)方法,對數據集進行過濾、檢索等數據集的再次處理。如上例中,使用filter()方法來 對數據集進行過濾。
3) Terminal經過最終(terminal)方法完成對數據集中元素的處理。如上例中,使用forEach()完成對過濾後元素的打印。
在一次聚合操做中,能夠有多個Intermediate,可是有且只有一個Terminal。也就是說,在對一個Stream能夠進行屢次轉換操做,並非每次都對Stream的每一個元素執行轉換。並不像for循環中,循環N次,其時間複雜度就是N。轉換操做是lazy(惰性求值)的,只有在Terminal操做執行時,纔會一次性執行。能夠這麼認爲,Stream 裏有個操做函數的集合,每次轉換操做就是把轉換函數放入這個集合中,在 Terminal 操做的時候循環 Stream 對應的集合,而後對每一個元素執行全部的函數。數組
Stream的操做分類
剛纔提到的Stream的操做有Intermediate、Terminal和Short-circuiting:併發
- Intermediate:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered
- Terminal:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator
- Short-circuiting: anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
一、建立流
若是是數組的話,可使用 Arrays.stream()
或者 Stream.of()
建立流;若是是集合的話,能夠直接使用 stream()
方法建立流,由於該方法已經添加到 Collection 接口中。ide
public class CreateStreamDemo { public static void main(String[] args) { String[] arr = new String[]{"武漢加油", "中國加油", "世界加油"}; Stream<String> stream = Arrays.stream(arr); stream = Stream.of("武漢加油", "中國加油", "世界加油"); List<String> list = new ArrayList<>(); list.add("武漢加油"); list.add("中國加油"); list.add("世界加油"); stream = list.stream(); } }
查看 Stream 源碼的話,你會發現 of() 方法內部其實調用了 Arrays.stream() 方法。函數
public static<T> Stream<T> of(T... values) { return Arrays.stream(values); }
另外,集合還能夠調用 parallelStream() 方法建立併發流,默認使用的是 ForkJoinPool.commonPool()線程池。工具
List<Long> aList = new ArrayList<>(); Stream<Long> parallelStream = aList.parallelStream();
二、操做流
Stream 類提供了不少有用的操做流的方法,我來挑一些經常使用的給你介紹一下。ui
1)過濾線程
經過 filter() 方法能夠從流中篩選出咱們想要的元素。code
public class FilterStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰倫"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); Stream<String> stream = list.stream().filter(element -> element.contains("王")); stream.forEach(System.out::println); } }
filter() 方法接收的是一個 Predicate(Java 8 新增的一個函數式接口,接受一個輸入參數返回一個布爾值結果)類型的參數,所以,咱們能夠直接將一個 Lambda 表達式傳遞給該方法,好比說 element -> element.contains("王") 就是篩選出帶有「王」的字符串。
forEach() 方法接收的是一個 Consumer(Java 8 新增的一個函數式接口,接受一個輸入參數而且無返回的操做)類型的參數,類名 :: 方法名是 Java 8 引入的新語法,System.out 返回 PrintStream 類,println 方法你應該知道是打印的。
stream.forEach(System.out::println); 至關於在 for 循環中打印,相似於下面的代碼:
for (String s : strs) { System.out.println(s); }
很明顯,一行代碼看起來更簡潔一些。來看一下程序的輸出結果:
王力宏
2)映射
若是想經過某種操做把一個流中的元素轉化成新的流中的元素,可使用 map() 方法。
public class MapStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰倫"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); Stream<Integer> stream = list.stream().map(String::length); stream.forEach(System.out::println); } }
map() 方法接收的是一個 Function(Java 8 新增的一個函數式接口,接受一個輸入參數 T,返回一個結果 R)類型的參數,此時參數 爲 String 類的 length 方法,也就是把 Stream<String> 的流轉成一個 Stream<Integer> 的流。
程序輸出的結果以下所示:
3 3 2 3
3)匹配
Stream 類提供了三個方法可供進行元素匹配,它們分別是:
anyMatch(),只要有一個元素匹配傳入的條件,就返回 true。
allMatch(),只有有一個元素不匹配傳入的條件,就返回 false;若是所有匹配,則返回 true。
noneMatch(),只要有一個元素匹配傳入的條件,就返回 false;若是所有匹配,則返回 true。
public class MatchStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰倫"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); boolean anyMatchFlag = list.stream().anyMatch(element -> element.contains("王")); boolean allMatchFlag = list.stream().allMatch(element -> element.length() > 1); boolean noneMatchFlag = list.stream().noneMatch(element -> element.endsWith("沉")); System.out.println(anyMatchFlag); System.out.println(allMatchFlag); System.out.println(noneMatchFlag); } }
由於「王力宏」以「王」字開頭,因此 anyMatchFlag 應該爲 true;由於「周杰倫」、「王力宏」、「陶喆」、「林俊杰」的字符串長度都大於 1,因此 allMatchFlag 爲 true;由於 4 個字符串結尾都不是「沉」,因此 noneMatchFlag 爲 true。
程序輸出的結果以下所示:
true true true
4)組合
reduce() 方法的主要做用是把 Stream 中的元素組合起來,它有兩種用法:
- Optional<T> reduce(BinaryOperator<T> accumulator)
沒有起始值,只有一個參數,就是運算規則,此時返回 Optional。
- T reduce(T identity, BinaryOperator<T> accumulator)
有起始值,有運算規則,兩個參數,此時返回的類型和起始值類型一致。
來看下面這個例子。
public class ReduceStreamDemo { public static void main(String[] args) { Integer[] ints = {0, 1, 2, 3}; List<Integer> list = Arrays.asList(ints); Optional<Integer> optional = list.stream().reduce((a, b) -> a + b); Optional<Integer> optional1 = list.stream().reduce(Integer::sum); System.out.println(optional.orElse(0)); System.out.println(optional1.orElse(0)); int reduce = list.stream().reduce(6, (a, b) -> a + b); System.out.println(reduce); int reduce1 = list.stream().reduce(6, Integer::sum); System.out.println(reduce1); } }
運算規則能夠是 Lambda 表達式(好比 (a, b) -> a + b),也能夠是類名::方法名(好比 Integer::sum)。
程序運行的結果以下所示:
6 6 12 12
0、一、二、3 在沒有起始值相加的時候結果爲 6;有起始值 6 的時候結果爲 12。
三、轉換流0
既然能夠把集合或者數組轉成流,那麼也應該有對應的方法,將流轉換回去——collect() 方法就知足了這種需求。
public class CollectStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰倫"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); String[] strArray = list.stream().toArray(String[]::new); System.out.println(Arrays.toString(strArray)); List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList()); List<String> list2 = list.stream().collect(Collectors.toCollection(ArrayList::new)); System.out.println(list1); System.out.println(list2); String str = list.stream().collect(Collectors.joining(", ")).toString(); System.out.println(str); } }
toArray() 方法能夠將流轉換成數組,你可能比較好奇的是 String[]::new,它是什麼東東呢?來看一下 toArray() 方法的源碼。
<A> A[] toArray(IntFunction<A[]> generator);
也就是說 String[]::new 是一個 IntFunction,一個能夠產生所需的新數組的函數,能夠經過反編譯字節碼看看它究竟是什麼:
String[] strArray = (String[])list.stream().toArray((x$0) -> { return new String[x$0]; }); System.out.println(Arrays.toString(strArray));
也就是至關於返回了一個指定長度的字符串數組。
當咱們須要把一個集合按照某種規則轉成另一個集合的時候,就能夠配套使用 map() 方法和 collect() 方法。
List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList());
經過 stream() 方法建立集合的流後,再經過 map(String:length) 將其映射爲字符串長度的一個新流,最後經過 collect() 方法將其轉換成新的集合。
Collectors 是一個收集器的工具類,內置了一系列收集器實現,好比說 toList() 方法將元素收集到一個新的 java.util.List 中;好比說 toCollection() 方法將元素收集到一個新的 java.util.ArrayList 中;好比說 joining() 方法將元素收集到一個能夠用分隔符指定的字符串中。
來看一下程序的輸出結果:
[周杰倫, 王力宏, 陶喆, 林俊杰] [3, 3, 2, 3] [周杰倫, 王力宏, 陶喆, 林俊杰] 周杰倫, 王力宏, 陶喆, 林俊杰