# Stream初體驗
1. Stream是元素的集合,這點讓Stream看起來用些類似Iterator;
2. 可以支持順序和并行的對原Stream進行匯聚的操作;
Stream相當于一個高級版本的Iterator。原始版本的Iterator,用戶只能一個一個的遍歷元素并對其執行某些操作;高級版本的Stream,用戶只要給出需要對其包含的元素執行什么操作,比如“過濾掉長度大于10的字符串”、“獲取每個字符串的首字母”等,具體這些操作如何應用到每個元素上,就給Stream就好了!
```
List<Integer> nums = Lists.newArrayList(1,null,3,4,null,6);
nums.stream().filter(num -> num != null).count();
```
上面這段代碼是獲取一個List中,元素不為null的個數。
[TOC]
# Stream通用語法

圖片就是對于Stream例子的一個解析,可以很清楚的看見:原本一條語句被三種顏色的框分割成了三個部分。紅色框中的語句是一個Stream的生命開始的地方,負責創建一個Stream實例;綠色框中的語句是賦予Stream靈魂的地方,把一個Stream轉換成另外一個Stream,紅框的語句生成的是一個包含所有nums變量的Stream,進過綠框的filter方法以后,重新生成了一個過濾掉原nums列表所有null以后的Stream;藍色框中的語句是豐收的地方,把Stream的里面包含的內容按照某種算法來匯聚成一個值,例子中是獲取Stream中包含的元素個數。
總結一下使用Stream的基本步驟:
1. 創建Stream;
2. 轉換Stream,每次轉換原有Stream對象不改變,返回一個新的Stream對象(**可以有多次轉換**);
3. 對Stream進行聚合(Reduce)操作,獲取想要的結果;
# 創建Stream
最常用的創建Stream有兩種途徑:
1. 通過Stream接口的靜態工廠方法
2. 通過Collection接口的默認方法–stream(),把一個Collection對象轉換成Stream
## 使用Stream靜態方法來創建Stream
1. Stream.of方法:有兩個overload方法,一個接受變長參數,一個接口單一值?
。
```
Stream<Integer> integerStream = Stream.of(1, 2, 3, 5);
Stream<String> stringStream = Stream.of("taobao");
```
2. generator方法:生成一個無限長度的Stream,其元素的生成是通過給定的Supplier(這個接口可以看成一個對象的工廠,每次調用返回一個給定類型的對象)
```
Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
});
Stream.generate(() -> Math.random());
Stream.generate(Math::random);
```
三條語句的作用都是一樣的,只是使用了lambda表達式和方法引用的語法來簡化代碼。每條語句其實都是生成一個無限長度的Stream,其中值是隨機的。這個無限長度Stream是懶加載,一般這種無限長度的Stream都會配合Stream的limit()方法來用。
3. iterate方法:也是生成無限長度的Stream,和generator不同的是,其元素的生成是重復對給定的種子值(seed)調用用戶指定函數來生成的。其中包含的元素可以認為是:seed,f(seed), f(f(seed))無限循環
```
Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println);
```
這段代碼就是先獲取一個無限長度的正整數集合的Stream,然后取出前10個打印。千萬記住使用limit方法,不然會無限打印下去。
## 通過Collection子類獲取Stream
Collection接口有一個stream方法,所以其所有子類都都可以獲取對應的Stream對象。
```
public interface Collection<E> extends Iterable<E> {
//其他方法省略
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
}
```
# 轉換Stream
轉換Stream其實就是把一個Stream通過某些行為轉換成一個新的Stream。Stream接口中定義了幾個常用的轉換方法。
1. distinct: 對于Stream中包含的元素進行去重操作(去重邏輯依賴元素的equals方法),新生成的Stream中沒有重復的元素;

2. filter: 對于Stream中包含的元素使用給定的過濾函數進行過濾操作,新生成的Stream只包含符合條件的元素;

3. map: 對于Stream中包含的元素使用給定的轉換函數進行轉換操作,新生成的Stream只包含轉換生成的元素。這個方法有三個對于原始類型的變種方法,分別是:mapToInt,mapToLong和mapToDouble。這三個方法也比較好理解,比如mapToInt就是把原始Stream轉換成一個新的Stream,這個新生成的Stream中的元素都是int類型。之所以會有這樣三個變種方法,可以免除自動裝箱/拆箱的額外消耗;

4. flatMap:和map類似,不同的是其每個元素轉換得到的是Stream對象,會把子Stream中的元素壓縮到父集合中;

5. peek: 生成一個包含原Stream的所有元素的新Stream,同時會提供一個消費函數(Consumer實例),新Stream每個元素被消費的時候都會執行給定的消費函數;

6. limit: 對一個Stream進行截斷操作,獲取其前N個元素,如果原Stream中包含的元素個數小于N,那就獲取其所有的元素;

7. skip: 返回一個丟棄原Stream的前N個元素后剩下元素組成的新Stream,如果原Stream中包含的元素個數小于N,那么返回空Stream;

8.連續使用
```
Stream<Integer> nums = Stream.of(1,1,null,2,3,4,null,5,6,7,8,9,10);
System.out.println(“sum is:”+nums.filter(num -> num != null).
distinct().mapToInt(num -> num * 2).
peek(System.out::println).skip(2).limit(4).sum());
```
這段代碼演示了上面介紹的所有轉換方法(除了flatMap),簡單解釋一下這段代碼的含義:給定一個Integer類型的Stream對象,然后進行過濾掉null,再去重,再每個元素乘以2,再每個元素被消費的時候打印自身,在跳過前兩個元素,最后取四個元素進行加和運算。
# Reduce Stream
Reduce Stream
接受一個元素序列為輸入,反復使用某個合并操作,把序列中的元素合并成一個匯總的結果。比如查找一個數字列表的總和或者最大值,或者把這些數字累積成一個List對象。Stream接口有一些通用的匯聚操作,比如reduce()和collect();也有一些特定用途的匯聚操作,比如sum(),max()和count()。
注意:sum方法不是所有的Stream對象都有的,只有IntStream、LongStream和DoubleStream是實例才有。
## 可變匯聚
可變匯聚對應的只有一個方法:collect,它可以把Stream中的要有元素收集到一個結果容器中(比如Collection)。先看一下最通用的collect方法的定義(還有其他override方法):
```
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
```
先來看看這三個參數的含義:Supplier supplier是一個工廠函數,用來生成一個新的容器;BiConsumer accumulator也是一個函數,用來把Stream中的元素添加到結果容器中;BiConsumer combiner還是一個函數,用來把中間狀態的多個結果容器合并成為一個(并發的時候會用到)。
```
Stream<Integer> nums = Stream.of(1,1,null,2,3,4,null,5,6,7,8,9,10);
List<Integer> numsWithoutNull = nums.filter(num -> num != null).
collect(() -> new ArrayList<Integer>(),
(list, item) -> list.add(item),
(list1, list2) -> list1.addAll(list2));
```
上面這段代碼就是對一個元素是Integer類型的Stream,先過濾掉全部的null,然后把剩下的元素收集到一個新的List中。進一步看一下collect方法的三個參數,都是lambda形式的函數。
* 第一個函數生成一個新的ArrayList實例;
* 第二個函數接受兩個參數,第一個是前面生成的ArrayList對象,二個是stream中包含的元素,函數體就是把stream中的元素加入ArrayList對象中。第二個函數被反復調用直到原stream的元素被消費完畢;
* 第三個函數也是接受兩個參數,這兩個都是ArrayList類型的,函數體就是把第二個ArrayList全部加入到第一個中;
但是上面的collect方法調用也有點太復雜了,看一下collect方法另外一個override的版本。
```
<R, A> R collect(Collector<? super T, A, R> collector);
```
Java8還給我們提供了Collector的工具類,其中已經定義了一些靜態工廠方法,比如:Collectors.toCollection()收集到Collection中,Collectors.toList()收集到List中和Collectors.toSet()收集到Set中。下面看看使用Collectors對于代碼的簡化:
```
List<Integer> numsWithoutNull = nums.filter(num -> num != null).
collect(Collectors.toList());
```
## 其他匯聚
1. reduce方法:reduce方法非常的通用,后面介紹的count,sum等都可以使用其實現。reduce方法有三個override的方法,先來看reduce方法的第一種形式,其方法定義如下:
```
Optional<T> reduce(BinaryOperator<T> accumulator);
```
接受一個BinaryOperator類型的參數,在使用的時候我們可以用lambda表達式來。
```
Stream<Integer> nums = Stream.of(1,2,3,4,5,6,7,8,9,10);
System.out.println("sum is:" + nums.reduce((sum, item) -> sum + item).get());
```
可以看到reduce方法接受一個函數,這個函數有兩個參數,第一個參數是上次函數執行的返回值(也稱為中間結果),第二個參數是stream中的元素,這個函數把這兩個值相加,得到的和會被賦值給下次執行這個函數的第一個參數。
要注意的是:**第一次執行的時候第一個參數的值是Stream的第一個元素,第二個參數是Stream的第二個元素**。這個方法返回值類型是Optional,這是Java8防止出現NullPointerException的一種可行方法。
> Java 8中所謂的Optional對象,即一個容器對象,該對象可以包含一個null或非null值。如果該值不為null,則調用isPresent()方法將返回true,且調用get()方法會返回該值。
可以使用orElse()方法給Optional對象設定默認值(當值為null時,會使用默認值
reduce方法還有一個很常用的變種:
```
T reduce(T identity, BinaryOperator<T> accumulator);
```
這個定義上上面已經介紹過的基本一致,不同的是:它允許用戶提供一個循環計算的初始值,如果Stream為空,就直接返回該值。而且這個方法不會返回Optional,因為其不會出現null值。
```
Stream<Integer> nums = Stream.of(1,2,3,4,5,6,7,8,9,10);
System.out.println("sum is:" + nums.reduce(0, (sum, item) -> sum + item).get());
```
2. count方法:獲取Stream中元素的個數
```
Stream<Integer> nums = Stream.of(1,2,3,4,5,6,7,8,9,10);
System.out.println("count is:" + nums.count());
```
3. 搜索相關
* allMatch:是不是Stream中的所有元素都滿足給定的匹配條件
* anyMatch:Stream中是否存在任何一個元素滿足匹配條件
* findFirst: 返回Stream中的第一個元素,如果Stream為空,返回空Optional
* noneMatch:是不是Stream中的所有元素都不滿足給定的匹配條件
* max和min:使用給定的比較器(Operator),返回Stream中的最大|最小值
```
Stream<Integer> stream1 = Stream.of(1,2,3,4,5,6,7,8,9,10);
System.out.printlnstream1 .allMatch(item -> item < 100));
Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6,7,8,9,10);
stream2 .max((o1, o2) ->o1.compareTo(o2)).ifPresent(System.out::println);
```