乐趣区

关于java:深度掌握-Java-Stream-流操作让你的代码高出一个逼格

概念

Stream将要解决的元素汇合看作一种流,在流的过程中,借助 Stream API 对流中的元素进行操作,比方:筛选、排序、聚合等。

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是并行流,外部以多线程并行执行的形式对流进行操作,但前提是流中的数据处理没有程序要求。

例如筛选汇合中的奇数,两者的解决不同之处:

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。看完了,心愿你能点个赞,哈哈。

退出移动版