天天看點

Java8新特性-Stream一、什麼是Stream二、建立Stream三、中間操作四、終端操作

代碼托管:https://github.com/stopping5/javaBase/blob/master/src/com/company/java8/StreamTest.java

一、什麼是Stream

1、定義

​ Java 8引入了一套新的類庫,位于包java.util.stream下,稱為Stream API。這套API操作資料的思路不同于我們之前介紹的容器類API,它們是函數式的,非常簡潔、靈活、易讀。

A sequence of elements supporting sequential and parallel aggregate operations.

​ 支援順序和并行聚合操作的元素序列。

​ 在java8中java.util.Collection新增了兩個預設方法,它們可以傳回一個Stream。順序流就是由一個線程執行操作。而并行流背後可能有多個線程并行執行。

//通過該集合傳回集合源的流
default Stream<E> stream()
//通過該集合傳回并行的流
default Stream<E> parallelStream()
           

2、Stream特性

  • Stream不會存儲元素
  • Stream不改變源對象,會根據相關操作傳回一個持有結果的新對象
  • Stream 操作是延遲執行的,意味着等待結果的時候才執行。

二、建立Stream

​ 通過集合、數組建立一個資料源。

通過

java.util.Collection.stream()

方法用集合建立流

List<String> list = Arrays.asList("a", "b", "c");
// 建立一個順序流
Stream<String> stream = list.stream();
// 建立一個并行流
Stream<String> parallelStream = list.parallelStream();
           

使用

java.util.Arrays.stream(T[] array)

方法用數組建立流

int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);
           

使用

Stream

的靜态方法:

of()、iterate()、generate()

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println); // 0 2 4 6 8 10

Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);
           

三、中間操作

​ 中間操作的目的主要是對資料進行操作,例如映射、統計、過濾等…

1、篩選操作

Filter

傳回predicate比對的流

案例:篩選年齡大于18并且薪資大于8000的員工

private static void filterDemo(List<Employee> employees) {
    employees.stream()
        //年齡大于18
        .filter((e)->{
            System.out.println("篩選大于18");
            return  e.getAge() > 18;
        })
        //并且薪資大于8000
        .filter((e)->{
            System.out.println("篩選大于8000");
            return e.getSalary() > 8000;
        })
        .limit(2)//&& 操作
        .forEach(
            (e)->{
                System.out.println(e.toString());
            }
    );
    //篩選大于18
    //篩選大于18
    //篩選大于18
    //篩選大于8000
    //Employee{name='張5', age=28, salary=18000.0}
    //篩選大于18
    //篩選大于8000
    //Employee{name='張6', age=19, salary=8400.0}
}
           

2、映射操作

- Map

接收lambda,将元素轉換成其他形式和提取資訊。接收一個函數作為參數,該函數會被應用到每個元素上。相當于資料庫中提取一列的資料。

<R> Stream<R> map(Function<? super T,? extends R> mapper)
------------------------------------------------------------
Type Parameters:
	R - The element type of the new stream
Parameters:
	mapper - a non-interfering, stateless function to apply to each element
Returns:
	the new stream
           

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-vY8vZVQi-1625380843937)(http://img.stopping.top/20210704123510.svg)]

類似的還有:mapToInt、mapToLong、mapToDouble

案例:在Employee擷取所有年齡組成新的流。

這裡通過employee.getAge()函數應用到每個元素上的結果。

private static void mapDemo(List<Employee> employees) {
    employees.stream().map(Employee::getAge).forEach(System.out::println);
}
//18 - 13 - 28 - 19 - 38 - 15 - 22
           

- FlatMap

接收一個函數作為參數,将流中每個值都換成另外一個流,然後把所有流連接配接成一個流。

案例

将流中的每個字元串輸出為字元。在FlatMap中直接就可以将每個元素的值轉化為一個流。在Map中通過調用flatMapStringToChar()将元素轉化為流,通過Stream<Stream(Charcyer)>相當于{{a,a,a},{b,b,b},{c,c,c}},而flatMap{a,a,a,b,b,b,c,c,c}兩者裡面的資料結構不一樣。

private static void flatMapDemo() {
    System.out.println("Map實作···");
    List<String> s = Arrays.asList("aaa","bbb","ccc");
    //map實作每個元素字元串轉化為字元流輸出
    Stream<Stream<Character>> ssc = s.stream().map(StreamTest::flatMapStringToChar);
    ssc.forEach((sc)->{
        sc.forEach(System.out::println);
    });
    System.out.println("flatMap實作···");
    //flatMap 實作每個元素字元串轉化為字元流輸出
    s.stream().flatMap(StreamTest::flatMapStringToChar).forEach(System.out::println);
}

public static Stream<Character> flatMapStringToChar(String s){
    List<Character> lc = new ArrayList<>();
    for (Character c : s.toCharArray()){
        lc.add(c);
    }
    return lc.stream();
}
//Map實作···
//a
//a
//a
//b
//b
//b
//c
//c
//c
//flatMap實作···
//a
//a
//a
//b
//b
//b
//c
//c
//c
           

3、切片操作

limit 截取

限制傳回長度不超過

maxSize

的流

Stream<T> limit(long maxSize)
-------------------------------------------------------------------
Parameters:
	maxSize - the number of elements the stream should be limited to
Returns:
	the new stream
           

skip 丢棄

丢棄流中前

n

個元素,如果丢棄的元素大于流的本身的元素則傳回一個空流。

distinct 去重

去重,傳回流中不同的元素(根據 Object.equals(Object))流

案例

4、收集操作

四、終端操作

​ 中間操作不觸發實際的執行,傳回值是Stream,而終端操作觸發執行,傳回一個具體的值,除了collect, Stream API的終端操作還有max、min、count、allMatch、anyMatch、noneMatch、findFirst、findAny、forEach、toArray、reduce等

MAX、MIN

Optional<T> max(Comparator<? super T> comparator)Optional<T> min(Comparator<? super T> comparator)-------------------------------------------------Parameters:	comparator - a non-interfering, stateless Comparator to compare elements of this streamReturns:	an Optional describing the maximum/minimum  element of this stream, or an empty Optional if the stream is empty
           

案例:擷取最大/最小年齡的員工資訊

Count

傳回流中元素的個數

allMatch/anyMatch/noneMatch

這幾個函數都接受一個謂詞Predicate,傳回一個boolean值,用于判定流中的元素是否滿足一定的條件。

它們的差別是:

  1. allMatch:隻有在流中所有元素都滿足條件的情況下才傳回true。
  2. anyMatch:隻要流中有一個元素滿足條件就傳回true。
  3. noneMatch:隻有流中所有元素都不滿足條件才傳回true。

如果流為空,那麼這幾個函數的傳回值都是true。

findFirst/findAny

Optional<T> findFirst()傳回流的第一個元素的 Optional,如果流為空,則傳回空的 Optional。Optional<T> findAny()傳回流的某些元素的 Optional,如果流為空,則傳回空的 Optional。-------------------------------------------------------------------Returns:	an Optional describing some element of this stream, or an empty Optional if the stream is emptyThrows:	NullPointerException - if the element selected is null
           

foreach

​ 周遊流中的元素,foreach和佛reachOrdered的差別:在并行流中,forEach不保證處理的順序,而forEachOrdered會保證按照流中元素的出現順序進行處理。

reduce

​ reduce代表歸約或者叫折疊,它是max/min/count的更為通用的函數,将流中的元素歸約為一個值。例如例如 sum()、max() 或 count()都是歸約函數。

Collectors

​ java.util.stream.Collectors 實作了Collector接口,提供了很多有用的歸約操作,例如将中間操作累積為集合、連接配接資料、分組、統計等。

用法示例:

groupingBy 分組

​ 分組類似于資料庫查詢語言SQL中的group by語句,它将元素流中的每個元素分到一個組,可以針對分組再進行處理和收集。

案例

​ 建立一個集合,裡面包括每個使用者的名稱、年齡、手機号。現在通過年齡來實作分組。

User.java 實體類

public class User {    /**     * 使用者名     */    private String username;    /**     * 年齡     */    private Integer age;    /**     * 手機号     */    private String phone;}
           
public static void main(String[] args) {    List<User> users = Arrays.asList(new User[]{            new User("tom",18,"13141234567"),            new User("job",20,"13141234567"),            new User("gogo",18,"13141234567")    });    Map<Integer,List<User>> userMap =     users.stream().collect(Collectors.groupingBy(User::getAge));    userMap.forEach((i,user)->{        System.out.println("age:"+i+","+"user:"+user.toString());    });}
           

結果

age:18,user:[User{username='tom', age=18, phone='13141234567'}, User{username='gogo', age=18, phone='13141234567'}]age:20,user:[User{username='job', age=20, phone='13141234567'}]
           

使用Stream group方法等同于下面方法實作,很顯然Steam Group更加的簡潔。

/** * 非Stream group 實作 * */private static void userGroup(List<User> users) {    Map<Integer,List<User>> userMap = new HashMap<>();    for (User u : users){        Integer age = u.getAge();        if (userMap.containsKey(age)){            userMap.get(age).add(u);        }else{            List<User> user = new ArrayList<>();            user.add(u);            userMap.put(age,user);        }    }    userMap.forEach((i,user)->{        System.out.println("age:"+i+","+"user:"+user.toString());    });}