1. 程式人生 > 其它 >Stream流式處理解釋

Stream流式處理解釋

Stream流

Stream 中文稱為 “流”,通過將集合轉換為這麼一種叫做 “流” 的元素序列,通過宣告性方式,能夠對集合中的每個元素進行一系列並行或序列的流水線操作。

函數語言程式設計帶來的好處尤為明顯。這種程式碼更多地表達了業務邏輯的意圖,而不是它的實現機制。易讀的程式碼也易於維護、更可靠、更不容易出錯。

面對一對多結構,查詢主實體時需要附帶主實體的子實體列表怎麼寫?查出主列表,迴圈差子列表

List的Stream流操作可以簡化我們的程式碼,減少程式執行的壓力,應對上面的問題,以前的話是先查出對應的list資料,然後根據取到集合中id去查詢對應的子實體中資料,接著在放入對應的集合中去,key值表示主實體的id,value值表示對應主實體id查到的結合資料,這樣就會三次foreach迴圈組裝資料,會很麻煩,當資料量大的時候,會增加程式執行的負荷,造成執行緩慢。所以,流式操作代替我們的這一堆操作,提高了程式碼的簡易性,可維護性,可靠性,更不容易出錯。

列子
首先我們先建立一個 Person 泛型的 List

List<UserPerson> list = new ArrayList<>();
list.add(new UserPerson("jack", 20));
list.add(new UserPerson("mike", 25));
list.add(new UserPerson("tom", 30));

UserPerson 類包含年齡和姓名兩個成員變數

public class UserPonson{
    private String name;
    private int age;
}
  1. stream() / parallelStream()
    最常用到的方法,將集合轉換為流
List list = new ArrayList();
// return Stream<E>
list.stream();

而 parallelStream() 是並行流方法,能夠讓資料集執行並行操作

  1. filter(T -> boolean)
    保留 boolean 為 true 的元素
保留年齡為 20 的 userPerson 元素
list = list.stream()
            .filter(person -> person.getAge() == 20)
            .collect(toList());

列印輸出 [UserPerson{name
='jack', age=20}]

collect(toList()) 可以把流轉換為 List 型別

  1. distinct()
    去除重複元素,這個方法是通過類的 equals 方法來判斷兩個元素是否相等的
    如例子中的 UserPerson 類,需要先定義好 equals 方法,不然類似[UserPerson{name='jack', age=20}, UserPerson{name='jack', age=20}] 這樣的情況是不會處理的
  2. sorted() / sorted((T, T) -> int)
    如果流中的元素的類實現了 Comparable 介面,即有自己的排序規則,那麼可以直接呼叫 sorted() 方法對元素進行排序,如 Stream<Integer>
    反之, 需要呼叫 sorted((T, T) -> int) 實現 Comparator 介面
根據年齡大小來比較:
list = list.stream()
           .sorted((p1, p2) -> p1.getAge() - p2.getAge())
           .collect(toList());

當然這個可以簡化為

list = list.stream()
           .sorted(Comparator.comparingInt(UserPerson::getAge))
           .collect(toList());
  1. limit(long n)
    返回前 n 個元素
list = list.stream()
            .limit(2)
            .collect(toList());

列印輸出 [UserPerson{name='jack', age=20}, UserPerson{name='mike', age=25}]
  1. skip(long n)
    去除前 n 個元素
list = list.stream()
            .skip(2)
            .collect(toList());

列印輸出 [UserPerson{name='abc', age=30}]

tips:

用在 limit(n) 前面時,先去除前 m 個元素再返回剩餘元素的前 n 個元素
limit(n) 用在 skip(m) 前面時,先返回前 n 個元素再在剩餘的 n 個元素中去除 m 個元素

list = list.stream()
            .limit(2)
            .skip(1)
            .collect(toList());

列印輸出 [UserPerson{name='mike', age=25}]
  1. map(T -> R)
    將流中的每一個元素 T 對映為 R(類似型別轉換)
List<String> newlist = list.stream().map(UserPerson::getName).collect(toList());

newlist 裡面的元素為 list 中每一個 UserPerson 物件的 name 變數

  1. flatMap(T -> Stream<R>)
    將流中的每一個元素 T 對映為一個流,再把每一個流連線成為一個流
List<String> list = new ArrayList<>();
list.add("aaa bbb ccc");
list.add("ddd eee fff");
list.add("ggg hhh iii");

list = list.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(toList());

上面例子中,我們的目的是把 List 中每個字串元素以" "分割開,變成一個新的 List<String>。
首先 map 方法分割每個字串元素,但此時流的型別為 Stream<String[ ]>,因為 split 方法返回的是 String[ ] 型別;所以我們需要使用 flatMap 方法,先使用Arrays::stream將每個 String[ ] 元素變成一個 Stream<String> 流,然後 flatMap 會將每一個流連線成為一個流,最終返回我們需要的 Stream<String>

  1. anyMatch(T -> boolean)
    流中是否有一個元素匹配給定的 T -> boolean 條件
    是否存在一個 person 物件的 age 等於 20:
boolean b = list.stream().anyMatch(person -> person.getAge() == 20);
  1. allMatch(T -> boolean)
    流中是否所有元素都匹配給定的 T -> boolean 條件
  2. noneMatch(T -> boolean)
    流中是否沒有元素匹配給定的 T -> boolean 條件
  3. findAny() 和 findFirst()

findAny():找到其中一個元素 (使用 stream() 時找到的是第一個元素;使用 parallelStream() 並行時找到的是其中一個元素)
findFirst():找到第一個元素

值得注意的是,這兩個方法返回的是一個 Optional<T> 物件,它是一個容器類,能代表一個值存在或不存在,這個後面會講到

  1. reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)
    用於組合流中的元素,如求和,求積,求最大值等
計算年齡總和:
int sum = list.stream().map(UserPerson::getAge).reduce(0, (a, b) -> a + b);
與之相同:
int sum = list.stream().map(UserPerson::getAge).reduce(0, Integer::sum);

其中,reduce 第一個引數 0 代表起始值為 0,lambda (a, b) -> a + b 即將兩值相加產生一個新值
同樣地:

計算年齡總乘積:
int sum = list.stream().map(UserPerson::getAge).reduce(1, (a, b) -> a * b);

當然也可以

Optional<Integer> sum = list.stream().map(UserPerson::getAge).reduce(Integer::sum);

即不接受任何起始值,但因為沒有初始值,需要考慮結果可能不存在的情況,因此返回的是 Optional 型別

  1. count()
    返回流中元素個數,結果為 long 型別
  2. collect()
    收集方法,我們很常用的是 collect(toList()),當然還有 collect(toSet()) 等,引數是一個收集器介面,這個後面會另外講
  3. forEach()
    返回結果為 void,很明顯我們可以通過它來幹什麼了,比方說:
### 16. unordered()
還有這個比較不起眼的方法,返回一個等效的無序流,當然如果流本身就是無序的話,那可能就會直接返回其本身

列印各個元素:
list.stream().forEach(System.out::println);

再比如說 MyBatis 裡面訪問資料庫的 mapper 方法:

向資料庫插入新元素:
list.stream().forEach(PersonMapper::insertPerson);

文章來源:https://www.jianshu.com/p/24af4f3ab046