Java8-StreamApi的详解

目录

1.Java8的Stream是什么?

2.流的构成:

 3.Stream的使用

 3.1 Stream的创建

3.2.1 Stream和parallelStream的简单区分:

3.3 Stream使用案例:

3.3.1 遍历匹配(foreach/find/match)

3.3.2 筛选(filter)

3.3.3聚合(max/min/count)

3.3.4 映射(map/flatMap) 

3.3.5 规约(reduce)

3.3.6 收集 (collect) 

3.3.7 提取组合(limit、skip、distince)

4.引用


1.Java8的Stream是什么?

        Stream作为Java8的亮点之一,与java.io包里的InputStream和OutputStream的概念完全不同。Java8中的Stream是容器对象功能的增强,它专注于对容器对象进行各种非常便利、高效的聚合操作,或者大批量数据操作。StreamApi借助于Java8出现的Lambda表达式,极大提高编程效率和程序可读性。同时,他提供串行和并行俩种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。通常,编写并行代码很难而且容易出错,但是用StreamApi无需编写一行多线程代码,就可以很方便地写出高并发性能的代码。

        Stream不是集合元素,它不是数据结构不保存数据,它是有关算法和计算的,它更像一个高级版本的Iterator。原始版本的Iterator用户只能显示地一个一个元素遍历并对其进行操作。高级版本的stream,用户只要给出需要对其包含的元素执行的操作即可,比如:“过滤掉长度大于10的字符串”、“获取每个字符串的首字母”等,Stream会隐式地在内部进行遍历,做出相应的数据转换。Stream就如同一个迭代器,单向不可往复,数据只能遍历一次,一次过后即用尽了,一去不返。

        Stream不同于Iterator的是,可以并行操作,迭代器只能命令式地、串行化操作。而使用并行去遍历的时候,会将数据分成多段,其中每一个都在不同线程中处理,然后将结果一起输出。Stream的并行操作依赖于Java7中引入的Fork/join框架(JSR166y)来拆分任务和加速处理过程。

        Stream另外一大特点就是,数据源本身可以是无限的。

2.流的构成:

        当我们使用一个流的时候,通常包括三个基本步骤:获取一个数据源->数据转换->执行操作获取想要的结果。每次转换原有的Stream对象不改变,返回一个新的Stream对象(可多次转换),这就允许对其操作可以像链条一样排列,变成一个管道,如下图:

Java8-StreamApi的详解_第1张图片

 3.Stream的使用

Stream可以由数组或集合创建,对流的操作分为两种:

  1. 中间操作,每次返回一个新的流,可以有多个。
  2. 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。

Stream的特性可归纳为:

  • 不是数据结构;

  • 它没有内部存储,它只是用操作管道从source(数据结构、数组、generator function、IO channel)抓取数据;

  • 它也绝不修改自己所封装的底层数据结构的数据。例如Stream的filter操作会产生一个不包含被过滤元素的新Stream,而不是从source删除那些元素;

  • 所有Stream的操作必须以lambda表达式为参数;

  • 不支持索引访问;

  • 你可以请求第一个元素,但无法请求第二个,第三个,或最后一个;

  • 很容易生成数组或者List;

  • 惰性化;

  • 很多Stream操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始;

  • Intermediate操作永远是惰性化的;

  • 并行能力;

  • 当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的;

  • 可以是无限的。集合有固定大小,Stream 则不必。limit(n)和findFirst()这类的short-circuiting操作可以对无限的Stream进行运算并很快完成。

脑图总结:

Java8-StreamApi的详解_第2张图片

 3.1 Stream的创建

 3.1.1Stream可通过集合、数组创建

//      1.通过java.util.Collection.stream()方法用集合创建流。
        List list = Arrays.asList("a","b","c");
        //创建一个顺序流
        Stream stream = list.stream();
        //创建一个并行流
        Stream parallelStream = list.parallelStream();
//      2.使用java.util.Arrays.stream(T[] array)方法用数组创建流
        int[] array = {1,2,3,4,5,6};
        IntStream arrayTostream = Arrays.stream(array);

//      3.使用Stream的静态方法:of()、iterate()、generate()
        Stream integerStream = Stream.of(1, 2, 3, 4, 5, 6);
        integerStream.forEach(System.out::print);
        System.out.println();
        Stream limitStream = Stream.iterate(0, (x) -> x + 3).limit(4);
        limitStream.forEach(System.out::print);
        System.out.println();
        Stream generateStream = Stream.generate(Math::random).limit(3);
        generateStream.forEach(System.out::println);

执行结果:

123456
0369
0.6411747078200662
0.9151646370590305
0.14447476425041816

3.2.1 Stream和parallelStream的简单区分:

stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如筛选集合中的奇书,俩这处理方式不同:

Java8-StreamApi的详解_第3张图片

 如果流中数据足够大,可以使用并行流处理加快速度。

并行流除了可以直接创建,后还可以通过parallel()将顺序流转换为并行流:

OptionalInt first = arrayTostream.parallel().filter(s -> s > 2).findFirst();

3.3 Stream使用案例:

创建一个员工类来进行StreamAPI的操作

public class Employee {

    /**
     * 姓名
     */
    private String name;
    /**
     * 年龄
     */
    private Integer age;
    /**
     * 薪资
     */
    private Double salary;
    /**
     * 性别
     */
    private String sex;
    /**
     * 地区
     */
    private String area;

    public Employee() {
    }

    public Employee(String name, Integer age, Double salary, String sex, String area) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.sex = sex;
        this.area = area;
    }

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

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

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

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

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

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public Double getSalary() {
        return salary;
    }

    public String getSex() {
        return sex;
    }

    public String getArea() {
        return area;
    }

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

3.3.1 遍历匹配(foreach/find/match)

        //3.1 遍历 匹配(foreach/find/match)
        //Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在的。Stream的遍历、匹配非常简单
        List integerList = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
        System.out.println("要操作的元素集合:" + integerList);
        System.out.println("遍历输出符合条件的元素,条件:大于6");
        integerList.stream().filter(i -> i > 6).forEach(System.out::println);
        Integer first = integerList.stream().filter(i -> i > 6).findFirst().get();
        System.out.println("匹配第一个:" + first);
        Integer anyInt = integerList.parallelStream().filter(i -> i > 6).findAny().get();
        System.out.println("匹配任意(适用于并行流):" + anyInt);
        System.out.println("是否包含符合特定条件的元素:");
        boolean anyMathc = integerList.stream().anyMatch(x -> x > 6);
        System.out.println("是否存在大于6的值?" + anyMathc);

输出结果:

要操作的元素集合:[7, 6, 9, 3, 8, 2, 1]
遍历输出符合条件的元素,条件:大于6
7
9
8
匹配第一个:7
匹配任意(适用于并行流):8
是否包含符合特定条件的元素:
是否存在大于6的值?true

3.3.2 筛选(filter)

 List employeeList = new ArrayList<>();
        employeeList.add(new Employee("瑞克",19,10000.0,"男","北京"));
        employeeList.add(new Employee("米琼恩",36,5673.2,"女","纽约"));
        employeeList.add(new Employee("玛姬",22,7332.0,"女","上海"));
        employeeList.add(new Employee("肖恩",38,10000.0,"男","华盛顿"));
        employeeList.add(new Employee("莫尔",32,9000.0,"男","佛罗里达"));
        employeeList.add(new Employee("格伦",23,7538.0,"男","首尔"));
        employeeList.add(new Employee("卡尔",16,4234.0,"男","芝加哥"));
        employeeList.add(new Employee("弩哥",50,12221.1,"男","拉斯维加斯"));

        System.out.println("所有员工:" + employeeList);

        List rt8kNameList = employeeList.stream().filter(e -> e.getSalary() > 8000).map(Employee::getName).collect(Collectors.toList());
        System.out.println("打印出工资超过8k的员工:" + rt8kNameList);
        System.out.println();

结果打印:

所有员工:[Employee{name='瑞克', age=19, salary=10000.0, sex='男', area='北京'}, Employee{name='米琼恩', age=36, salary=5673.2, sex='女', area='纽约'}, Employee{name='玛姬', age=22, salary=7332.0, sex='女', area='上海'}, Employee{name='肖恩', age=38, salary=10000.0, sex='男', area='华盛顿'}, Employee{name='莫尔', age=32, salary=9000.0, sex='男', area='佛罗里达'}, Employee{name='格伦', age=23, salary=7538.0, sex='男', area='首尔'}, Employee{name='卡尔', age=16, salary=4234.0, sex='男', area='芝加哥'}, Employee{name='弩哥', age=50, salary=12221.1, sex='男', area='拉斯维加斯'}]
打印出工资超过8k的员工:[瑞克, 肖恩, 莫尔, 弩哥]

3.3.3聚合(max/min/count)

List stringList = Arrays.asList("admin", "admn", "pig", "rabbit", "rocketmq", "as", "p");
        String maxLength = stringList.stream().max(Comparator.comparing(String::length)).get();
        System.out.println("最长的字符串:"+maxLength);
        Optional max = integerList.stream().max(Integer::compareTo);
        System.out.println("integerList中最大的值:" + max.get());

        Employee maxSalary = employeeList.stream().max(Comparator.comparingDouble(Employee::getSalary)).get();
        System.out.println("最高的工资为:"+ maxSalary.getSalary());

        long count = integerList.stream().filter(i -> i > 6).count();
        System.out.println("integerList集合中大于6的个数:" + count);

结果:

最长的字符串:rocketmq
integerList中最大的值:9
最高的工资为:12221.1
integerList集合中大于6的个数:3

3.3.4 映射(map/flatMap) 

可以将一个六种元素按照一定规则映射到另一个流中。分为map、platMap

map 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素

flatMap:接受一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流连接成一个流

        List newStringList = stringList.stream().map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("stringList元素都变成大写:" + newStringList);
        List newIntegerList = integerList.stream().map(x -> x + 3).collect(Collectors.toList());
        System.out.println("integerList每个元素+3:" + newIntegerList);
        //员工工资+1k
        Map newSalaryMap = employeeList.stream().map(e -> {
            Employee employee = new Employee(e.getName(), e.getAge(), e.getSalary() + 1000, e.getSex(), e.getArea());
            return employee;
        }).collect(Collectors.toMap(Employee::getName, Employee::getSalary));
        System.out.println("员工工资+1000:" + newSalaryMap);

        List strings = Arrays.asList("a,v,c,e", "w,r,t");
        List newStrings = strings.stream().flatMap(s -> {
            String[] split = s.split(",");
            Stream s2 = Arrays.stream(split);
            return s2;
        }).collect(Collectors.toList());
        System.out.println("之前:" + strings);
        System.out.println("之后:" + newStrings);

结果:

stringList元素都变成大写:[ADMIN, ADMN, PIG, RABBIT, ROCKETMQ, AS, P]
integerList每个元素+3:[10, 9, 12, 6, 11, 5, 4]
员工工资+1000:{米琼恩=6673.2, 瑞克=11000.0, 格伦=8538.0, 弩哥=13221.1, 卡尔=5234.0, 莫尔=10000.0, 肖恩=11000.0, 玛姬=8332.0}
之前:[a,v,c,e, w,r,t]
之后:[a, v, c, e, w, r, t]

3.3.5 规约(reduce)

能实现对集合求和,求乘积和求最值操作

 //求和方式1
        Optional sum = integerList.stream().reduce((i1, i2) -> i1 + i2);
        //求和方式2
        Optional sum2 = integerList.stream().reduce(Integer::sum);
        //求和方式3
        Integer sum3 = integerList.stream().reduce(0, Integer::sum);

        //求乘积
        Optional reduce = integerList.stream().reduce((i1, i2) -> i1 * i2);

        //求最大值
        Optional max1 = integerList.stream().reduce((i1, i2) -> i1 > i2 ? i1 : i2);
        //求最大值方式2
        Integer max2 = integerList.stream().reduce(1, Integer::max);
        System.out.println("integerList求和:" + sum.get() + "," + sum2.get() + "," + sum3);
        System.out.println("integerList乘积:" + reduce.get());
        System.out.println("integerList最大值:" + max1.get()+ "," + max2);

        //员工共工资之和1
        Optional sSum1 = employeeList.stream().map(Employee::getSalary).reduce(Double::sum);
        //员工共工资之和2
        Double sSum2 = employeeList.stream().reduce(0.0, (ssum, e) -> ssum += e.getSalary(), (ssum1, ssum2) -> ssum1 + ssum2);
        //员工共工资之和3
        Double sSum3 = employeeList.stream().reduce(0.0, (sumSa, e) -> sumSa += e.getSalary(), Double::sum);

        //员工最高工资1
        Double maxSa1 = employeeList.stream().reduce(0.0, (maxSa, e) -> maxSa > e.getSalary() ? maxSa : e.getSalary(), Double::max);
        //员工最高工资1
        Double maxSa2 = employeeList.stream().reduce(0.0, (maxSa, e) -> maxSa > e.getSalary() ? maxSa : e.getSalary(), (m1, m2) -> m1 > m2 ? m1 : m2);
        
        System.out.println("员工工资之和:" + sSum1.get() + "," + sSum2 + "," + sSum3);
        System.out.println("员工最大工资:" + maxSa1 + "," + maxSa2);

结果:

integerList求和:36,36,36
integerList乘积:18144
integerList最大值:9,9
员工工资之和:65998.3,65998.3,65998.3
员工最大工资:12221.1,12221.1

3.3.6 收集 (collect) 

collect,收集,可以说是内容最繁多,功能最丰富的部分了。从字面意思理解,就是把一个流收集起来,最终可以是收集成一个值,也可以是收集成一个新的集合。

collect主要依赖于java.util.stream.Collectors类内置的静态方法

Collectors提供了一系列用于数据统计的静态方法 

计数:count

平均值:averagingInt、averagingLong、averagingDouble 

最值:maxBy、minBy 

求和:summingInt、summingLong、summingDouble

统计以上所有:summarizingInt、summarizingLong、summarizingDouble

 Map ageMap = employeeList.stream().filter(x -> x.getAge() % 2 == 0).collect(Collectors.toMap(Employee::getName, Employee::getAge));
        System.out.println("年龄为奇数的员工集合:" + ageMap);
        Set set = employeeList.stream().filter(e -> e.getAge() % 2 == 0).collect(Collectors.toSet());
        System.out.println("员工转为set集合:"+set);

        Map gt8kSalaryMap = employeeList.stream().filter(e -> e.getSalary() > 8000).collect(Collectors.toMap(Employee::getName, Employee::getSalary, (e1, e2) -> e1));
        System.out.println(gt8kSalaryMap);

        //求员工总数
        Long empCount = employeeList.stream().collect(Collectors.counting());
        System.out.println("员工总数:"+empCount);
        //求平均工资
        Double avgSalary = employeeList.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println("平均工资:" + avgSalary);
        //求最高工资
        Double maxSalary1 = employeeList.stream().collect(Collectors.maxBy(Comparator.comparing(Employee::getSalary))).get().getSalary();
        Optional maxSalary2 = employeeList.stream().map(Employee::getSalary).collect(Collectors.maxBy(Double::compare));
        System.out.println("最高工资:" + maxSalary1 + "," + maxSalary2.get());
        //求工资之和
        Double sumSalary = employeeList.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println("工资之和:" + sumSalary);
        //一次性统计所有信息
        DoubleSummaryStatistics collect = employeeList.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println("员工工资所有统计:" + collect);
年龄为奇数的员工集合:{米琼恩=36, 弩哥=50, 卡尔=16, 莫尔=32, 肖恩=38, 玛姬=22}
员工转为set集合:[Employee{name='肖恩', age=38, salary=10000.0, sex='男', area='华盛顿'}, Employee{name='玛姬', age=22, salary=7332.0, sex='女', area='上海'}, Employee{name='米琼恩', age=36, salary=5673.2, sex='女', area='纽约'}, Employee{name='卡尔', age=16, salary=4234.0, sex='男', area='芝加哥'}, Employee{name='弩哥', age=50, salary=12221.1, sex='男', area='拉斯维加斯'}, Employee{name='莫尔', age=32, salary=9000.0, sex='男', area='佛罗里达'}]
{瑞克=10000.0, 弩哥=12221.1, 莫尔=9000.0, 肖恩=10000.0}
员工总数:8
平均工资:8249.7875
最高工资:12221.1,12221.1
工资之和:65998.3
员工工资所有统计:DoubleSummaryStatistics{count=8, sum=65998.300000, min=4234.000000, average=8249.787500, max=12221.100000}

分组(partitioningBy/groupingBy)

分区:将stream按条件分为俩个Map,比如员工按照薪资是否高于8000分为2个部分

 分组:将集合分为多个map,比如员工按照性别分组。有单级分组和多级分组。

        Map> isGt8kMap = employeeList.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 8000));
        System.out.println("员工是否工资大于8k分组后:" + isGt8kMap);

        Map> ageMap1 = employeeList.stream().collect(Collectors.groupingBy(Employee::getSex));
        System.out.println("员工按照性别分组:" + ageMap);

        Map>> sexAreaMap = employeeList.stream().collect(Collectors.groupingBy(Employee::getSex, Collectors.groupingBy(Employee::getArea)));
        System.out.println("员工按照性别分组后再按照地区分组:" + sexAreaMap);

结果:

员工是否工资大于8k分组后:{false=[Employee{name='米琼恩', age=36, salary=5673.2, sex='女', area='纽约'}, Employee{name='玛姬', age=22, salary=7332.0, sex='女', area='上海'}, Employee{name='格伦', age=23, salary=7538.0, sex='男', area='首尔'}, Employee{name='卡尔', age=16, salary=4234.0, sex='男', area='芝加哥'}], true=[Employee{name='瑞克', age=19, salary=10000.0, sex='男', area='北京'}, Employee{name='肖恩', age=38, salary=10000.0, sex='男', area='华盛顿'}, Employee{name='莫尔', age=32, salary=9000.0, sex='男', area='佛罗里达'}, Employee{name='弩哥', age=50, salary=12221.1, sex='男', area='拉斯维加斯'}]}
员工按照性别分组:{米琼恩=36, 弩哥=50, 卡尔=16, 莫尔=32, 肖恩=38, 玛姬=22}
员工按照性别分组后再按照地区分组:{女={上海=[Employee{name='玛姬', age=22, salary=7332.0, sex='女', area='上海'}], 纽约=[Employee{name='米琼恩', age=36, salary=5673.2, sex='女', area='纽约'}]}, 男={拉斯维加斯=[Employee{name='弩哥', age=50, salary=12221.1, sex='男', area='拉斯维加斯'}], 华盛顿=[Employee{name='肖恩', age=38, salary=10000.0, sex='男', area='华盛顿'}], 芝加哥=[Employee{name='卡尔', age=16, salary=4234.0, sex='男', area='芝加哥'}], 首尔=[Employee{name='格伦', age=23, salary=7538.0, sex='男', area='首尔'}], 佛罗里达=[Employee{name='莫尔', age=32, salary=9000.0, sex='男', area='佛罗里达'}], 北京=[Employee{name='瑞克', age=19, salary=10000.0, sex='男', area='北京'}]}}

join接合 joining可以将stream中的元素用特定的连接符(没有的话直接连接)连接成一个字符串

String empNameList = employeeList.stream().map(Employee::getName).collect(Collectors.joining(","));
        System.out.println("所有员工名字:" + empNameList);
        String collect1 = stringList.stream().collect(Collectors.joining("-"));
        System.out.println("stringList拼接后:" + collect1);

结果:

所有员工名字:瑞克,米琼恩,玛姬,肖恩,莫尔,格伦,卡尔,弩哥
stringList拼接后:admin-admn-pig-rabbit-rocketmq-as-p

Collectors.reduce()规约 相比于stream本身的reduce,增加了对自定义规约的支持

Double taxableSalarySum = employeeList.stream().collect(Collectors.reducing(0.0, Employee::getSalary, (i, j) -> (i + j - 5000)));
        System.out.println("员工应扣税金额总和:"+ taxableSalarySum);

结果:

员工应扣税金额总和:25998.300000000003

排序(Sorted)

        List sortList = employeeList.stream().sorted(Comparator.comparing(Employee::getSalary)).collect(Collectors.toList());
        System.out.println("员工工资升序排序:" + sortList);
        List reverseSortList = employeeList.stream().sorted(Comparator.comparing(Employee::getSalary).reversed()).collect(Collectors.toList());
        System.out.println("员工工资降序排序:" + reverseSortList);

        List salaryAgeNameList = employeeList.stream().sorted(Comparator.comparing(Employee::getSalary).thenComparing(Employee::getAge)).map(Employee::getName).collect(Collectors.toList());
        System.out.println("优先工资然后年龄升序排序:" + salaryAgeNameList);

结果:

员工工资升序排序:[Employee{name='卡尔', age=16, salary=4234.0, sex='男', area='芝加哥'}, Employee{name='米琼恩', age=36, salary=5673.2, sex='女', area='纽约'}, Employee{name='玛姬', age=22, salary=7332.0, sex='女', area='上海'}, Employee{name='格伦', age=23, salary=7538.0, sex='男', area='首尔'}, Employee{name='莫尔', age=32, salary=9000.0, sex='男', area='佛罗里达'}, Employee{name='瑞克', age=19, salary=10000.0, sex='男', area='北京'}, Employee{name='肖恩', age=38, salary=10000.0, sex='男', area='华盛顿'}, Employee{name='弩哥', age=50, salary=12221.1, sex='男', area='拉斯维加斯'}]
员工工资降序排序:[Employee{name='弩哥', age=50, salary=12221.1, sex='男', area='拉斯维加斯'}, Employee{name='瑞克', age=19, salary=10000.0, sex='男', area='北京'}, Employee{name='肖恩', age=38, salary=10000.0, sex='男', area='华盛顿'}, Employee{name='莫尔', age=32, salary=9000.0, sex='男', area='佛罗里达'}, Employee{name='格伦', age=23, salary=7538.0, sex='男', area='首尔'}, Employee{name='玛姬', age=22, salary=7332.0, sex='女', area='上海'}, Employee{name='米琼恩', age=36, salary=5673.2, sex='女', area='纽约'}, Employee{name='卡尔', age=16, salary=4234.0, sex='男', area='芝加哥'}]
优先工资然后年龄升序排序:[卡尔, 米琼恩, 玛姬, 格伦, 莫尔, 瑞克, 肖恩, 弩哥]

3.3.7 提取组合(limit、skip、distince)

        String[] arr1 = {"a","b","c","d","e"};
        String[] arr2 = {"e","d","h","i","j"};
        Stream stream1 = Stream.of(arr1);
        Stream stream2 = Stream.of(arr2);
        List newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
        System.out.println("Stream合并去重:" + newList);
        List newIntList = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        System.out.println("限制流从流中获取10个数字:" + newIntList);
        List skipNIntList = Stream.iterate(1, x -> x + 2).skip(4).limit(5).collect(Collectors.toList());
        System.out.println("跳过前N个数据并且限制获取5个数字:" + skipNIntList);

结果:

Stream合并去重:[a, b, c, d, e, h, i, j]
限制流从流中获取10个数字:[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
跳过前N个数据并且限制获取5个数字:[9, 11, 13, 15, 17]

4.引用

Java8中的Stream API详解:Stream的背景及使用

Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合

你可能感兴趣的:(java8,java,开发语言,后端)