1. 程式人生 > >Java 8程式設計進階-Stream之函數語言程式設計

Java 8程式設計進階-Stream之函數語言程式設計

1、什麼是Stream

       Java 8 API添加了一個新的抽象稱為流Stream,可以讓你以一種宣告的方式處理資料。Stream 使用一種類似用 SQL 語句從資料庫查詢資料的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。Stream API可以極大提高Java程式設計師的生產力,讓程式設計師寫出高效率、乾淨、簡潔的程式碼。這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。元素流在管道中經過中間操作(intermediate operation)的處理,最後由最終操作(terminal operation)得到前面處理的結果。

    總之,中間操作只是建立另一個流,不會執行任何處理,直到最終操作被呼叫。一旦最終操作被呼叫,則開始遍歷所有的流,並且相關的函式會逐一應用到流上。中間操作是惰性操作,所以,流支援惰性,下面是一些函式的分類。

Stream操作分類
中間操作(Intermediate operations)無狀態(Stateless)unordered() filter() map() mapToInt() mapToLong() mapToDouble() flatMap() flatMapToInt() flatMapToLong() flatMapToDouble() peek()
有狀態(Stateful)distinct() sorted() sorted() limit() skip()
最終操作(Terminal operations)非短路操作forEach() forEachOrdered() toArray() reduce() collect() max() min() count()
短路操作(short-circuiting)anyMatch() allMatch() noneMatch() findFirst() findAny()
2、流的使用

2.1、獲取Stream

在使用流之前,首先需要擁有一個數據源,並通過StreamAPI提供的一些方法獲取該資料來源的流物件。資料來源可以有多種形式:

(1)集合 
這種資料來源較為常用,通過stream()方法即可獲取流物件:

   List<String> list = new ArrayList<>();
   Stream<String> stream = list.stream();

(2)陣列 
通過Arrays類提供的靜態函式stream()獲取陣列的流物件:

   String[] array = {"a","b","c"};
   Stream<String> stream = Arrays.stream(array);

(3)值 
直接將幾個值變成流物件:

   Stream<String> stream = Stream.of("a""b""c");

2.2、forEach

對流中的每個元素執行一些操作。

    List<String> list = new ArrayList<>(Arrays.asList("a","b","c"));
    //寫法一
    list.stream().forEach(x -> {
        System.out.println(x);
    });
    //寫法二
    list.stream().forEach(System.out::println);

2.3、map

接收一個函式作為引數,該函式會被應用到每個元素上,並將其對映成一個新的元素

    List<String> list = new ArrayList<>(Arrays.asList("a","b","c"));
    //寫法一
    list.stream().forEach(x -> {
        System.out.println(x);
    });
    //寫法二
    list.stream().forEach(System.out::println);

2.4、flatMap

接收一個函式作為引數,將流中的每個值都換成另一個流,然後把所有流連線成一個流。

    List<String> list = new ArrayList<>(Arrays.asList("a,1","b,2","c,3"));

    List<String> newList = list.stream().flatMap(x->{
        String[] array = x.split(",");
        return Arrays.stream(array);
    }).collect(Collectors.toList());

結果:

[a, 1, b, 2, c, 3]

2.5、reduce()

通過一個二進位制操作將流中的元素合併到一起。

    List<Integer> list = new ArrayList<>(Arrays.asList(123));
    //寫法一,累加不帶初始值
    Optional accResult = list.stream().reduce((acc, item) -> {
        acc += item;
        return acc;
    });

輸出6

    //寫法一,累加帶初始值10
    int result1 = list.stream().reduce(10(acc, item) -> {
        acc += item;
        return acc;
    });

輸出16

2.6、filter()

過濾元素

    List<Integer> list = new ArrayList<>(Arrays.asList(123,4));
    List<Integer> newList = list.stream().filter(x -> x >=3).collect(Collectors.toList());

輸出[3,4]

2.7、distinct()

去除重複元素

    List<Integer> list = new ArrayList<>(Arrays.asList(111,2,2));
    List<Integer> newList = list.stream().distinct().collect(Collectors.toList());

輸出結果

[1,2]

2.8、collect() 

將流中的元素傾倒入某些容器

(1)toList

將元素轉成List,很簡單,上面的例項很多

(2)toSet

將元素轉成Set

    List<Integer> list = new ArrayList<>(Arrays.asList(11122));
    Set<Integerset = list.stream().collect(Collectors.toSet());

 (3) toMap

將元素轉成Map

用法可以如下

public Map<Long, String> getIdNameMap(List<Account> accounts) {

return accounts.stream().collect(Collectors.toMap(Account::getId, Account::getUsername));
}

收整合實體本身map

程式碼如下:

public Map<Long, Account> getIdAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getId, account -> account));
}

account -> account是一個返回本身的lambda表示式,其實還可以使用Function介面中的一個預設方法代替,使整個方法更簡潔優雅:

public Map<Long, Account> getIdAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getId, Function.identity()));
}

重複key的情況

程式碼如下:

public Map<String, Account> getNameAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity()));
}

這個方法可能報錯(java.lang.IllegalStateException: Duplicate key),因為name是有可能重複的。toMap有個過載方法,可以傳入一個合併的函式來解決key衝突問題:

public Map<String, Account> getNameAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity()(key1, key2) -> key2));
}

這裡只是簡單的使用後者覆蓋前者來解決key重複問題。

指定具體收集的map

toMap還有另一個過載方法,可以指定一個Map的具體實現,來收集資料:

public Map<String, Account> getNameAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity()(key1, key2) -> key2LinkedHashMap::new));
}

2.8、min()  
根據一個比較器找到流中元素的最小值。

BigDecimal min = planList.stream().min((a,b)->a.getPrice().compareTo(b.getPrice())).get().getPrice();

2.9、max() 

根據一個比較器找到流中元素的最大值。

BigDecimal max = planList.stream().max((a,b)->a.getPrice().compareTo(b.getPrice())).get().getPrice();

2.10、count() 

計算流中元素的數量。

    List<Integer> list = new ArrayList<>(Arrays.asList(14233,101,6));
    long min = list.stream().distinct().count();