深度掌握 Java Stream 流操作,讓你的程式碼高出一個逼格!

初念初戀發表於2021-08-02

概念

Stream將要處理的元素集合看作一種流,在流的過程中,藉助Stream API對流中的元素進行操作,比如:篩選、排序、聚合等。

image-20210701194245361

Stream 的操作符大體上分為兩種:中間操作符終止操作符

中間操作符

對於資料流來說,中間操作符在執行制定處理程式後,資料流依然可以傳遞給下一級的操作符。

中間操作符包含8種(排除了parallel,sequential,這兩個操作並不涉及到對資料流的加工操作):

  1. map(mapToInt,mapToLong,mapToDouble) 轉換操作符,把比如A->B,這裡預設提供了轉int,long,double的操作符。
  2. flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) 拍平操作比如把 int[]{2,3,4} 拍平 變成 2,3,4 也就是從原來的一個資料變成了3個資料,這裡預設提供了拍平成int,long,double的操作符。
  3. limit 限流操作,比如資料流中有10個 我只要出前3個就可以使用。
  4. distint 去重操作,對重複元素去重,底層使用了equals方法。
  5. filter 過濾操作,把不想要的資料過濾。
  6. peek 挑出操作,如果想對資料進行某些操作,如:讀取、編輯修改等。
  7. skip 跳過操作,跳過某些元素。
  8. sorted(unordered) 排序操作,對元素排序,前提是實現Comparable介面,當然也可以自定義比較器。

終止操作符

資料經過中間加工操作,就輪到終止操作符上場了;

終止操作符就是用來對資料進行收集或者消費的,資料到了終止操作這裡就不會向下流動了,終止操作符只能使用一次。

  1. collect 收集操作,將所有資料收集起來,這個操作非常重要,官方的提供的Collectors 提供了非常多收集器,可以說Stream 的核心在於Collectors。
  2. count 統計操作,統計最終的資料個數。
  3. findFirst、findAny 查詢操作,查詢第一個、查詢任何一個 返回的型別為Optional。
  4. noneMatch、allMatch、anyMatch 匹配操作,資料流中是否存在符合條件的元素 返回值為bool 值。
  5. min、max 最值操作,需要自定義比較器,返回資料流中最大最小的值。
  6. reduce 規約操作,將整個資料流的值規約為一個值,count、min、max底層就是使用reduce。
  7. forEach、forEachOrdered 遍歷操作,這裡就是對最終的資料進行消費了。
  8. toArray 陣列操作,將資料流的元素轉換成陣列。

Stream的建立

1、通過 java.util.Collection.stream() 方法用集合建立流

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

2、使用java.util.Arrays.stream(T[] array)方法用陣列建立流

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

3、使用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 3 6 9

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

輸出結果:

3
6
9
0.8106623442686114
0.11554643727388458
0.1404645961428974

Process finished with exit code 0

streamparallelStream的簡單區分:

stream是順序流,由主執行緒按順序對流執行操作;
parallelStream是並行流,內部以多執行緒並行執行的方式對流進行操作,但前提是流中的資料處理沒有順序要求。

例如篩選集合中的奇數,兩者的處理不同之處:

image-20210701230623951

Stream使用

遍歷/匹配(foreach/find/match)

Stream也是支援類似集合的遍歷和匹配元素的,只是Stream中的元素是以Optional型別存在的。Stream的遍歷、匹配非常簡單。

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
        // 遍歷輸出符合條件的元素
        list.stream().filter(x -> x > 6).forEach(System.out::println);
        // 匹配第一個
        Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
        // 匹配任意(適用於並行流)
        Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
        // 是否包含符合特定條件的元素
        boolean anyMatch = list.stream().anyMatch(x -> x < 6);
        System.out.println("匹配第一個值:" + findFirst.get());
        System.out.println("匹配任意一個值:" + findAny.get());
        System.out.println("是否存在大於6的值:" + anyMatch);
        
    }
}

輸出結果:

7
9
8
匹配第一個值:7
匹配任意一個值:8
是否存在大於6的值:true

Process finished with exit code 0

篩選(filter)

篩選,是按照一定的規則校驗流中的元素,將符合條件的元素提取到新的流中的操作。

篩選出Integer集合中大於7的元素,並列印出來

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
        Stream<Integer> stream = list.stream();
        stream.filter(x -> x > 7).forEach(System.out::println);
    }
}

輸出結果:

8
9

Process finished with exit code 0

聚合(max/min/count)

maxmincount這些字眼你一定不陌生,沒錯,在mysql中我們常用它們進行資料統計。Java stream中也引入了這些概念和用法,極大地方便了我們對集合、陣列的資料統計工作。

案例一:獲取String集合中最長的元素。

public class StreamTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
        Optional<String> max = list.stream().max(Comparator.comparing(String::length));
        System.out.println("最長的字串:" + max.get());
    }
}

輸出結果:

最長的字串:weoujgsd

Process finished with exit code 0

案例二:獲取Integer集合中的最大值。

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 9, 4, 11, 6);
        // 自然排序
        Optional<Integer> max = list.stream().max(Integer::compareTo);
        // 自定義排序
        Optional<Integer> max2 = list.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println("自然排序的最大值:" + max.get());
        System.out.println("自定義排序的最大值:" + max2.get());
    }
}

輸出結果:

自然排序的最大值:11
自定義排序的最大值:11

Process finished with exit code 0

案例三:計算Integer集合中大於6的元素的個數。

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 4, 8, 2, 11, 9);
        long count = list.stream().filter(x -> x > 6).count();
        System.out.println("list中大於6的元素個數:" + count);
    }
}

輸出結果:

list中大於6的元素個數:4

Process finished with exit code 0

對映(map/flatMap)

對映,可以將一個流的元素按照一定的對映規則對映到另一個流中。分為mapflatMap

  • map:接收一個函式作為引數,該函式會被應用到每個元素上,並將其對映成一個新的元素。
  • flatMap:接收一個函式作為引數,將流中的每個值都換成另一個流,然後把所有流連線成一個流。

案例一:英文字串陣列的元素全部改為大寫。整數陣列每個元素+3。

public class StreamTest {

    public static void main(String[] args) {
        String[] strArr = { "abcd", "bcdd", "defde", "fTr" };
        List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("每個元素大寫:" + strList);
        List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
        List<Integer> intListNew = intList.stream().map(x -> x + 3).collect(Collectors.toList());
        System.out.println("每個元素+3:" + intListNew);
    }
}

輸出結果:

每個元素大寫:[ABCD, BCDD, DEFDE, FTR]
每個元素+3:[4, 6, 8, 10, 12, 14]

Process finished with exit code 0

案例二:將兩個字元陣列合併成一個新的字元陣列。

public class StreamTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
        List<String> listNew = list.stream().flatMap(s -> {
            // 將每個元素轉換成一個stream
            String[] split = s.split(",");
            Stream<String> s2 = Arrays.stream(split);
            return s2;
        }).collect(Collectors.toList());
        System.out.println("處理前的集合:" + list);
        System.out.println("處理後的集合:" + listNew);
    }
}

輸出結果:

處理前的集合:[m,k,l,a, 1,3,5,7]
處理後的集合:[m, k, l, a, 1, 3, 5, 7]

Process finished with exit code 0

歸約(reduce)

歸約,也稱縮減,顧名思義,是把一個流縮減成一個值,能實現對集合求和、求乘積和求最值操作。

案例一:求Integer集合的元素之和、乘積和最大值。

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
        // 求和方式1
        Optional<Integer> sum = list.stream().reduce(Integer::sum);
        // 求和方式2
        Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
        // 求和方式3
        Integer sum3 = list.stream().reduce(0, Integer::sum);
        // 求乘積
        Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
        // 求最大值方式1
        Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
        // 求最大值寫法2
        Integer max2 = list.stream().reduce(1, Integer::max);
        System.out.println("list求和:" + sum.get() + "," + sum2.get() + "," + sum3);
        System.out.println("list求積:" + product.get());
        System.out.println("list求和:" + max.get() + "," + max2);
    }
}

輸出結果:

list求和:29,29,29
list求積:2112
list求和:11,11

Process finished with exit code 0

歸集(toList/toSet/toMap)

因為流不儲存資料,那麼在流中的資料完成處理後,需要將流中的資料重新歸集到新的集合裡。toListtoSettoMap比較常用,另外還有toCollectiontoConcurrentMap等複雜一些的用法。

下面用一個案例演示toListtoSettoMap

public class Person {

    private String name;  // 姓名
    private int salary; // 薪資
    private int age; // 年齡
    private String sex; //性別
    private String area;  // 地區

    // 構造方法
    public Person(String name, int salary, int age,String sex,String area) {
        this.name = name;
        this.salary = salary;
        this.age = age;
        this.sex = sex;
        this.area = area;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", area='" + area + '\'' +
                '}';
    }
}
public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
        List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
        Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        Map<?, Person> map = personList.stream().filter(p -> p.getSalary() > 8000)
                .collect(Collectors.toMap(Person::getName, p -> p));
        System.out.println("toList:" + listNew);
        System.out.println("toSet:" + set);
        System.out.println("toMap:" + map);
    }
}

輸出結果:

toList:[6, 4, 6, 6, 20]
toSet:[4, 20, 6]
toMap:{Tom=Person{name='Tom', salary=8900, age=23, sex='male', area='New York'}, Anni=Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}}

Process finished with exit code 0

統計(count/averaging)

Collectors提供了一系列用於資料統計的靜態方法:

  • 計數:count
  • 平均值:averagingIntaveragingLongaveragingDouble
  • 最值:maxByminBy
  • 求和:summingIntsummingLongsummingDouble
  • 統計以上所有:summarizingIntsummarizingLongsummarizingDouble

案例:統計員工人數、平均工資、工資總額、最高工資。

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        // 求總數
        long count = personList.size();
        // 求平均工資
        Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        // 求最高工資
        Optional<Integer> max = personList.stream().map(Person::getSalary).max(Integer::compare);
        // 求工資之和
        int sum = personList.stream().mapToInt(Person::getSalary).sum();
        // 一次性統計所有資訊
        DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
        System.out.println("員工總數:" + count);
        System.out.println("員工平均工資:" + average);
        System.out.println("員工最高工資:" + max.get());
        System.out.println("員工工資總和:" + sum);
        System.out.println("員工工資所有統計:" + collect);
    }
}

輸出結果:

員工總數:3
員工平均工資:7900.0
員工最高工資:8900
員工工資總和:23700
員工工資所有統計:DoubleSummaryStatistics{count=3, sum=23700.000000, min=7000.000000, average=7900.000000, max=8900.000000}

Process finished with exit code 0

分組(partitioningBy/groupingBy)

  • 分割槽:將stream按條件分為兩個Map,比如員工按薪資是否高於8000分為兩部分。
  • 分組:將集合分為多個Map,比如員工按性別分組。有單級分組和多級分組。

案例:將員工按薪資是否高於8000分為兩部分;將員工按性別和地區分組

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "Washington"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "New York"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        // 將員工按薪資是否高於8000分組
        Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 將員工按性別分組
        Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
        // 將員工先按性別分組,再按地區分組
        Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
        System.out.println("員工按薪資是否大於8000分組情況:" + part);
        System.out.println("員工按性別分組情況:" + group);
        System.out.println("員工按性別、地區:" + group2);
    }
}

輸出結果:

員工按薪資是否大於8000分組情況:{false=[Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'}, Person{name='Lily', salary=7800, age=21, sex='female', area='New York'}], true=[Person{name='Tom', salary=8900, age=23, sex='male', area='Washington'}, Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}]}
員工按性別分組情況:{female=[Person{name='Lily', salary=7800, age=21, sex='female', area='New York'}, Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}], male=[Person{name='Tom', salary=8900, age=23, sex='male', area='Washington'}, Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'}]}
員工按性別、地區:{female={New York=[Person{name='Lily', salary=7800, age=21, sex='female', area='New York'}, Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}]}, male={Washington=[Person{name='Tom', salary=8900, age=23, sex='male', area='Washington'}, Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'}]}}

Process finished with exit code 0

接合(joining)

joining可以將stream中的元素用特定的連線符(沒有的話,則直接連線)連線成一個字串。

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        String names = personList.stream().map(Person::getName).collect(Collectors.joining(","));
        System.out.println("所有員工的姓名:" + names);
        List<String> list = Arrays.asList("A", "B", "C");
        String string = list.stream().collect(Collectors.joining("-"));
        System.out.println("拼接後的字串:" + string);
    }
}

輸出結果:

所有員工的姓名:Tom,Jack,Lily
拼接後的字串:A-B-C

Process finished with exit code 0

排序(sorted)

sorted,中間操作。有兩種排序:

  • sorted():自然排序,流中元素需實現Comparable介面
  • sorted(Comparator com)Comparator排序器自定義排序

案例:將員工按工資由高到低(工資一樣則按年齡由大到小)排序

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Sherry", 9000, 24, "female", "New York"));
        personList.add(new Person("Tom", 8900, 22, "male", "Washington"));
        personList.add(new Person("Jack", 9000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 8800, 26, "male", "New York"));
        personList.add(new Person("Alisa", 9000, 26, "female", "New York"));
        // 按工資升序排序(自然排序)
        List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
                .collect(Collectors.toList());
        // 按工資倒序排序
        List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
                .map(Person::getName).collect(Collectors.toList());
        // 先按工資再按年齡升序排序
        List<String> newList3 = personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
                .collect(Collectors.toList());
        // 先按工資再按年齡自定義排序(降序)
        List<String> newList4 = personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }
        }).map(Person::getName).collect(Collectors.toList());
        System.out.println("按工資升序排序:" + newList);
        System.out.println("按工資降序排序:" + newList2);
        System.out.println("先按工資再按年齡升序排序:" + newList3);
        System.out.println("先按工資再按年齡自定義降序排序:" + newList4);
    }
}

輸出結果:

按工資升序排序:[Lily, Tom, Sherry, Jack, Alisa]
按工資降序排序:[Sherry, Jack, Alisa, Tom, Lily]
先按工資再按年齡升序排序:[Lily, Tom, Sherry, Jack, Alisa]
先按工資再按年齡自定義降序排序:[Alisa, Jack, Sherry, Tom, Lily]

Process finished with exit code 0

提取/組合

流也可以進行合併、去重、限制、跳過等操作。

public class StreamTest {

    public static void main(String[] args) {
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };
        Stream<String> stream1 = Stream.of(arr1);
        Stream<String> stream2 = Stream.of(arr2);
        // concat:合併兩個流 distinct:去重
        List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
        // limit:限制從流中獲得前n個資料
        List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        // skip:跳過前n個資料
        List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
        System.out.println("流合併:" + newList);
        System.out.println("limit:" + collect);
        System.out.println("skip:" + collect2);
    }
}

輸出結果:

流合併:[a, b, c, d, e, f, g]
limit:[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
skip:[3, 5, 7, 9, 11]

Process finished with exit code 0

分頁操作

stream api 的強大之處還不僅僅是對集合進行各種組合操作,還支援分頁操作。

例如,將如下的陣列從小到大進行排序,排序完成之後,從第1行開始,查詢10條資料出來,操作如下:

//需要查詢的資料
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5, 10, 6, 20, 30, 40, 50, 60, 100);
List<Integer> dataList = numbers.stream().sorted(Integer::compareTo).skip(0).limit(10).collect(Collectors.toList());
System.out.println(dataList.toString());

輸出結果:

[2, 2, 3, 3, 3, 5, 6, 7, 10, 20]

Process finished with exit code 0

並行操作

所謂並行,指的是多個任務在同一時間點發生,並由不同的cpu進行處理,不互相搶佔資源;而併發,指的是多個任務在同一時間點內同時發生了,但由同一個cpu進行處理,互相搶佔資源。

stream api 的並行操作和序列操作,只有一個方法區別,其他都一樣,例如下面我們使用parallelStream來輸出空字串的數量:

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
// 採用平行計算方法,獲取空字串的數量
long count = strings.parallelStream().filter(String::isEmpty).count();
System.out.println(count);

在實際使用的時候,並行操作不一定比序列操作快!對於簡單操作,數量非常大,同時伺服器是多核的話,建議使用Stream並行!反之,採用序列操作更可靠!

集合轉Map操作

在實際的開發過程中,還有一個使用最頻繁的操作就是,將集合元素中某個主鍵欄位作為key,元素作為value,來實現集合轉map的需求,這種需求在資料組裝方面使用的非常多。

public static void main(String[] args) {
    List<Person> personList = new ArrayList<>();
    personList.add(new Person("Tom",7000,25,"male","安徽"));
    personList.add(new Person("Jack",8000,30,"female","北京"));
    personList.add(new Person("Lucy",9000,40,"male","上海"));
    personList.add(new Person("Airs",10000,40,"female","深圳"));
    Map<Integer, Person> collect = personList.stream().collect(Collectors.toMap(Person::getAge, v -> v, (k1, k2) -> k1));
    System.out.println(collect);
}

輸出結果:

{40=Person{name='Lucy', salary=9000, age=40, sex='male', area='上海'}, 25=Person{name='Tom', salary=7000, age=25, sex='male', area='安徽'}, 30=Person{name='Jack', salary=8000, age=30, sex='female', area='北京'}}

Process finished with exit code 0

開啟Collectors.toMap方法原始碼,一起來看看。

public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction) {
    return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}

從參數列可以看出:

  • 第一個引數:表示 key
  • 第二個引數:表示 value
  • 第三個引數:表示某種規則

上文中的Collectors.toMap(Person::getAge, v -> v, (k1,k2) -> k1),表達的意思就是將age的內容作為keyv -> v是表示將元素person作為value,其中(k1,k2) -> k1表示如果存在相同的key,將第一個匹配的元素作為內容,第二個捨棄!

結尾

本文主要,圍繞 jdk stream api 操作,結合實際的日常開發需求,做了簡單總結和分享。希望你也能跟著一起敲一遍加深印象,相信都能掌握這些操作符的初步用法;後續文章我會帶大家一步步深入Stream。看完了,希望你能點個贊,哈哈。

相關文章