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


概念

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。看完了,希望你能點個贊,哈哈。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM