JDK8辅助学习(七):IntStream


本章节我们提供一些 Java 8 中的 IntStreamLongStreamDoubleStream 使用范例。IntStream、LongStream 和 DoubleStream 分别表示原始 int 流、 原始 long 流 和 原始 double 流。

这三个原始流类在 java.util.stream 命名空间下,并提供了大量的方法用于操作流中的数据,同时提供了相应的静态方法来初始化它们自己

本文仅介绍IntStream的方法,LongStream/DoubleStream 的使用同 IntStream 一致,此处不再过多介绍。


1.创建IntStream流(IntStream中的静态方法:of / builder/ range / rangeClosed / generate / iterate)

 创建IntStream流的几种方式,如下:

1. IntStream.of()方式

// 支持一个参数,也支持可变参数
IntStream intStream = IntStream.of(1);
IntStream intStream = IntStream.of(1,2,3,4,5,6,7);

2. IntStream.builder()方式 (了解即可)

// 1.可以使用add()方法添加元素
IntStream builderStream= builder.add(111).add(222).add(333).add(444).build();
// 2.也可以使用accept()方式添加,add()方法底层也是通过accept()方式添加的(这种方式不支持链式调用,此处了解一下即可)
IntStream.Builder builder = IntStream.builder();
builder.accept(3);
builder.accept(4);
builder.accept(5);
IntStream builderStream = builder.build();

3. IntStream.range()、IntStream.rangeClosed()方式
 将指定范围内的元素都添加到int流中
 range()前者不包含最后一个元素,即:前闭后开区间
 rangeClosed()后者包含,即:前闭后闭区间

// 支持一个参数,也支持可变参数
IntStream range = IntStream.range(1, 100);    // 返回 1,2,3,4,......,99
IntStream range = IntStream.rangeClosed(1, 100);    // 返回 1,2,3,4,......,99,100

4. IntStream.generate()方式 (了解即可)

// 1.这种方式,是无限连续的流,会源源不断的生成100内的随机数(持续不断输出)
IntStream generate1 = IntStream.generate(() -> new Random().nextInt(100));
// 2.使用limit()会限制流中元素的数量
IntStream generate1 = IntStream.generate(() -> new Random().nextInt(100)).limit(6);

5. IntStream.iterate()方式
 指定生成int流中int元素的生成函数,前者的生成函数没有入参,后者会将前一次调用结果作为下一次调用生成函数的入参
 第一个参数seed,就是第一次调用生成函数的入参

// 规则:生成2的幂次方int流
// 根据指定规则,生成一串int流(使用iterate()方式,也是无限连续的流,需使用limit()来限制元素的数量)
IntStream intStream11 = IntStream.iterate(1,x->{
	int a = 2 * x;
	return a;
}).limit(6);
// 简写:
IntStream intStream11 = IntStream.iterate(1,x->2 * x).limit(6);

6. IntStream.concat()方式(将两个int流合并成一个流)

// 将A流和B流合并,生成新流
IntStream range1 = IntStream.range(1, 5);   // A流
IntStream range2 = IntStream.range(10, 15);   // B流
IntStream concat = IntStream.concat(range1, range2);   // 合并后的流

2.filter

filter() 方法,用于过滤数据,返回符合过滤条件的数据,是一个非终结方法。我们可以通过 filter() 方法将一个流转换成另一个子集流。该接口接收一个 Predicate 函数式接口参数(可以是一个 Lambda 或 方法引用) 作为筛选条件。因为 filter() 是一个非终结方法,所以必须调用终止方法。示例如下:

@Test
public void test(){
    // 获取1-9的int流
    IntStream intStream = IntStream.range(1, 10);
    // 通过filter方式过滤掉小于5的数字,并输出
    intStream.filter(x->x>5).forEach(System.out::println);
}

输出结果:

6
7
8
9

3.map

IntStream 中的map()方法用于对流中的所有元素执行某个修改操作,这与 Stream 中的map()方法有所不同。Stream 中的map() 方法,参考:JDK8新特性(三):集合之 Stream 流式操作

@Test
public void test02() {
    IntStream intStream = IntStream.range(1, 10);
    // 使用map(),将流中的数据*2返回
    intStream.map(new IntUnaryOperator() {
        @Override
        public int applyAsInt(int value) {
            return 2 * value;
        }
    }).forEach(System.out::println);

    // 简写
    intStream.map(o -> 2 * o).forEach(System.out::println);
}

输出结果:

2
4
6
8
10
12
14
16
18

4.flatMap

flatMap() 方法同 map() 方法。

区别在于:flatMap() 方法的返回值是一个 IntStream 而非 int 值,可以在返回的IntStream 中包含多个元素,flatMap() 方法最终返回的 IntStream 是将每次调用 flatMap() 方法返回的 IntStream 合并后的结果

@Test
public void test03() {
    IntStream intStream = IntStream.range(1, 10);
    // 使用flatMap(),将流中的数据*2返回
    intStream.flatMap(new IntFunction<IntStream>() {
        @Override
        public IntStream apply(int value) {
            return IntStream.of(2 * value);
        }
    }).forEach(System.out::println);
    // 简写
    intStream.flatMap(o -> IntStream.of(2 * o)).forEach(System.out::println);
}

输出结果:

2
4
6
8
10
12
14
16
18

5.peek

介绍:该方法会生成一个包含原 Stream 的所有元素的新 Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数;

存在此方法的目的,主要是为了在您需要的地方支持调试,查看元素流过管道中特定点的情况

主要用于开发过程中调试使用!!!

@Test
public void test04() {
    IntStream intStream = IntStream.range(1, 10);
    long count = intStream.filter(x -> x > 5)
            .peek(val -> System.out.println("是否>5:" + val))
            .count();
	System.out.println("过滤后总数为:" + count);
}

输出结果:

是否>5:6
是否>5:7
是否>5:8
是否>5:9
过滤后总数为:4

6.mapToObj / mapToLong / mapToDouble / asLongStream / asDoubleStream

1.mapToObj:将 int 流转换成其他类型的流 (其他类型:可以使自定义对象类型,也可以是List类型等)
2.mapToLong:将 int 流转换成Long类型的流,不可指定返回值规则  (挺简单的,此处不做demo演示)
3.mapToDouble:将 int 流转换成Double类型的流,不可指定返回值规则  (挺简单的,此处不做demo演示)
4.asLongStream:将 int 流转换成Long类型的流,该方法遵循标准的int到指定类型的转换规则,不能指定规则,比如说将int流对象先 乘以2再返回(挺简单的,此处不做demo演示)
5.asDoubleStream:将 int 流转换成Double类型的流,该方法遵循标准的int到指定类型的转换规则,不能指定规则,比如说将int流对象先 乘以2再返回(挺简单的,此处不做demo演示)

// mapToObj()方法演示
@Test
public void test06() {
    IntStream intStream = IntStream.range(1, 10);
    // mapToObj():将int流转换成Person对象
    intStream.mapToObj(val -> new Person(val, "Mary" + val))
            .forEach(System.out::println);
}

// Person类
public class Person {

    private int id;

    private String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
// mapToLong / mapToDouble / asLongStream / asDoubleStream()方法演示
// 一个stream流只能被消费一次,以下是伪代码,会报错。
@Test
public void test07() {
    IntStream intStream = IntStream.range(1, 10);
    // mapToLong()方法(可指定返回值规则)
    intStream.mapToLong(val -> 2 * val).forEach(System.out::println);
    
    // mapToDouble()方法(可指定返回值规则)
    intStream.mapToDouble(val -> 2 * val).forEach(System.out::println);
    
    // asDoubleStream()方法(不可指定返回值规则)
    intStream.asDoubleStream().forEach(System.out::println);
    
    // asLongStream()方法(不可指定返回值规则)
    intStream.asLongStream().forEach(System.out::println);
}

7.forEach / forEachOrdered

这两个方法都是用来遍历流中的元素,是一个终结方法。该方法接收一个 IntConsumer 接口函数,会将每一个流元素交给该函数进行处理。
1.forEach:遍历流中的元素
2.mapToLongforEachOrderedforEach的区别在于并行流处理下,forEachOrdered会保证实际的处理顺序与流中元素的顺序一致,而forEach方法无法保证,默认的串行流处理下,两者无区别,都能保证处理顺序与流中元素顺序一致。

@Test
public void test08() {
    IntStream intStream = IntStream.of(2,6,3,5,9,8,1).parallel();
    intStream.forEach(x->{
        System.out.println("forEach->"+x);
    });

    // forEachOrdered()同forEach()方法
    // 区别:在于并行流处理下,forEachOrdered会保证实际的处理顺序与流中元素的顺序一致
    // 而forEach方法无法保证,默认的串行流处理下,两者无区别,都能保证处理顺序与流中元素顺序一致
    IntStream intStream1 = IntStream.of(2,6,3,5,9,8,1).parallel();
    intStream1.forEachOrdered(x->{
        System.out.println("forEachOrdered->"+x);
    });
}

输出结果:

forEach->9
forEach->5
forEach->2
forEach->1
forEach->8
forEach->3
forEach->6
forEachOrdered->2
forEachOrdered->6
forEachOrdered->3
forEachOrdered->5
forEachOrdered->9
forEachOrdered->8
forEachOrdered->1

8.distinct

distinct() 方法,可以用来去除重复数据。因为 distinct() 方法是一个非终结方法,所以必须调用终止方法。

@Test
public void test09() {
    IntStream intStream = IntStream.of(1, 2, 3, 4, 5, 6, 1, 4, 5, 2);
    intStream.distinct().forEach(System.out::println);
}

输出结果:

1
2
3
4
5
6

9.sorted

sorted() 方法,可以用来对 Stream 流中的数据进行排序。
Stream流中的sorted()方法,还允许我们自定义Comparator规则进行排序;参考:JDK8新特性(三):集合之 Stream 流式操作 中的sorted()方法
IntStream流中的sorted()方法,就是纯粹对int数值进行排序,所以也没有Comparator自定义比较器排序的必要

@Test
public void test10() {
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);
    intStream.sorted().forEach(System.out::println);
}

输出结果:

1
2
4
5
9
35

10.limit

limit()方法,用来对 Stream 流中的数据进行截取,只取用前 n 个,是一个非终结方法。参数是一个 long 型,如果集合当前长度大于参数则进行截取,否则不进行操作。因为 limit() 是一个非终结方法,所以必须调用终止方法

@Test
public void test11() {
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);
    // limit()截取,截取IntStream流中的前4个元素
    intStream.limit(4).forEach(System.out::println);
}

输出结果:

1
9
4
35

11.skip

如果希望跳过前几个元素,去取后面的元素,则可以使用 skip()方法,获取一个截取之后的新流,它是一个非终结方法。参数是一个 long 型,如果 Stream 流的当前长度大于 n,则跳过前 n 个,否则将会得到一个长度为 0 的空流。因为 limit() 是一个非终结方法,所以必须调用终止方法。

@Test
public void test11() {
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);
    // skip()跳过流中前2个元素,获取剩下的元素
    intStream.skip(2).forEach(System.out::println);
}

输出结果:

4
35
5
2

备注:

   skip() 和 limit() 配合使用,即可实现类似分页的操作了

//一页10条 分页操作
//第一页
skip(0).limit(10)
//第二页
skip(10).limit(10)
//第三页
skip(20).limit(10)
...

12.reduce

reduce方法,用于执行类似于累加的操作,上一次调用处理函数的结果会作为入参下一次调用的入参;

reduce方法,有如下两个不同参数的方法供调用

int reduce(int identity, IntBinaryOperator op);
OptionalInt reduce(IntBinaryOperator op);

参数说明:
1.int identity:初始值(是否需要设置初始值,视情况而定)
2.IntBinaryOperator op:计算规则

方法说明:
1.方法1有初始值,返回值为int
2.方法2无初始值,有可能为空的情况,所以返回 OptionalInt 类型

@Test
public void test22() {
    // IntStream流
    IntStream intStream = IntStream.of(1, 1, 1, 1, 1, 1);

    // 1.设置初始值为10,(求和则会在10的基础上进行计算,计算结果为16,返回值类型为int)
    // 基础写法
    int reduce = intStream.reduce(10, new IntBinaryOperator() {
        @Override
        public int applyAsInt(int left, int right) {
            return left + right;
        }
    });
    System.out.println(reduce);    // 16

    // 简写
    IntStream intStream1 = IntStream.of(1, 1, 1, 1, 1, 1);
    int reduce1 = intStream1.reduce(10, Integer::sum);
    System.out.println(reduce1);    // 16
    
    // 2.无初始值(因为返回值可能为空,所以返回类型为OptionInt)
    // 因为没有初始值,int类型默认为0,所以计算结果为6
    IntStream intStream2 = IntStream.of(1, 1, 1, 1, 1, 1);
    OptionalInt reduce2 = intStream2.reduce(Integer::sum);
    System.out.println(reduce2.getAsInt());    // 6
}

分析过程:
在这里插入图片描述

13.sum / min / max / count / average / summaryStatistics

sum:求和
min:求最小值
max:求最大值
count:计算IntStream流中元素个数
average:求平均值
summaryStatistics:该方法一次调用获取上述5个属性值

@Test
public void test11() {
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);
    // sum:求和
    int sum = intStream.sum();
    System.out.println("sum -> " + sum);
    
    // min:最小值
    IntStream intStream1 = IntStream.of(1, 9, 4, 35, 5, 2);
    OptionalInt min = intStream1.min();
    System.out.println("min -> " + min.getAsInt());
    
    // max:最大值
    IntStream intStream2 = IntStream.of(1, 9, 4, 35, 5, 2);
    OptionalInt max = intStream2.max();
    System.out.println("max -> " + max.getAsInt());
    
    // count:统计流中元素个数
    IntStream intStream3 = IntStream.of(1, 9, 4, 35, 5, 2);
    long count = intStream3.count();
    System.out.println("count -> " + count);
    
    // average:统计流中元素个数
    IntStream intStream4 = IntStream.of(1, 9, 4, 35, 5, 2);
    OptionalDouble average = intStream4.average();
    System.out.println("average -> " + average.getAsDouble());
    
    // summaryStatistics:汇总方法,一次性返回sum/min/max/count/average值
    // average:统计流中元素个数
    IntStream intStream5 = IntStream.of(1, 9, 4, 35, 5, 2);
    IntSummaryStatistics intSummaryStatistics = intStream5.summaryStatistics();
    System.out.println("summaryStatistics -> " + intSummaryStatistics.toString());
    System.out.println("summaryStatistics.sum -> " + intSummaryStatistics.getSum());
    System.out.println("summaryStatistics.min -> " + intSummaryStatistics.getMin());
    System.out.println("summaryStatistics.max -> " + intSummaryStatistics.getMax());
    System.out.println("summaryStatistics.count -> " + intSummaryStatistics.getCount());
    System.out.println("summaryStatistics.average -> " + intSummaryStatistics.getAverage());
}

输出结果:

sum -> 56
min -> 1
max -> 35
count -> 6
average -> 9.333333333333334
summaryStatistics -> IntSummaryStatistics{count=6, sum=56, min=1, average=9.333333, max=35}
summaryStatistics.sum -> 56
summaryStatistics.min -> 1
summaryStatistics.max -> 35
summaryStatistics.count -> 6
summaryStatistics.average -> 9.333333333333334

14.anyMatch / allMatch / noneMatch

这三个方法,均返回 boolean 类型
allMatch:流中的所有元素都满足给定的匹配条件
anyMatch:流中是否存在任何一个元素满足匹配条件
noneMatch:流中的所有元素都不满足给定的匹配条件

@Test
public void test12() {
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);
    // allMatch:流中是否所有元素都 > 10
    boolean allMatch = intStream.allMatch(val -> val > 10);
    System.out.println("allMatch -> " + allMatch);
    
    // anyMatch:流中是否存在一个元素 > 10
    IntStream intStream1 = IntStream.of(1, 9, 4, 35, 5, 2);
    boolean anyMatch = intStream1.anyMatch(val -> val > 10);
    System.out.println("anyMatch -> " + anyMatch);
    
    // noneMatch:流中元素是否都不满足给定条件(流中元素都<10)
    IntStream intStream2 = IntStream.of(1, 9, 4, 35, 5, 2);
    boolean noneMatch = intStream2.noneMatch(val -> val > 10);
    System.out.println("noneMatch -> " + noneMatch);
}

输出结果:

allMatch -> false
anyMatch -> true
noneMatch -> false

15.findFirst / findAny

IntStream流中的这两个方法,均返回 OptionalInt 类型
findFirst:返回IntStream流中的第一个元素,如果流中数据为空,返回空OptionalInt
findAny返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果如果是并行的情况,那就不能确保是第一个

@Test
public void test13() {
    // findFirst:返回流中的第一个元素
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);
    OptionalInt first = intStream.findFirst();
    System.out.println("findFirst -> " + first.getAsInt());

    // findAny:串行地情况下,一般会返回第一个结果.如果是并行的情况,那就不能确保是第一个
    IntStream intStream1 = IntStream.of(1, 9, 4, 35, 5, 2).parallel();
    OptionalInt any = intStream1.findAny();
    System.out.println("findAny -> " + any.getAsInt());
}

输出结果:

findFirst -> 1
findAny -> 35

16.sequential / parallel

sequential:串行(默认为串行,可以不使用sequential()方法)
parallel:并行

/**
 * sequential:串行
 */
@Test
public void test14() {
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);
    
    long start = System.currentTimeMillis();
    intStream.sequential().forEach(val -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":" + val);
    });
    long end = System.currentTimeMillis();
    System.out.println("sequential串行,用时:" + (end - start) + "ms");
}

输出结果:

main:1
main:9
main:4
main:35
main:5
main:2
sequential串行,用时:6037ms

分析:

sequential串行:只有main主线程执行,共耗时6037ms

/**
 * parallel:并行
 */
@Test
public void test15() {
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);

    long start = System.currentTimeMillis();
    intStream.parallel().forEach(val -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":" + val);
    });
    long end = System.currentTimeMillis();
    System.out.println("parallel并行,用时:" + (end - start) + "ms");
}

执行结果:

ForkJoinPool.commonPool-worker-2:2
ForkJoinPool.commonPool-worker-3:1
ForkJoinPool.commonPool-worker-1:9
ForkJoinPool.commonPool-worker-5:4
ForkJoinPool.commonPool-worker-4:5
main:35
parallel并行,用时:1033ms

分析:

parallel并行:共启动6个线程,共耗时1033ms

17.iterator

iterator: 迭代器,遍历返回流中的元素

@Test
public void test16() {
    // iterator:迭代器
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);
    PrimitiveIterator.OfInt iterator = intStream.iterator();
    while (iterator.hasNext()) {
        Integer next = iterator.next();
        System.out.println("iterator迭代数据:" + next);
    }
}

输出结果:

iterator迭代数据:1
iterator迭代数据:9
iterator迭代数据:4
iterator迭代数据:35
iterator迭代数据:5
iterator迭代数据:2

18.boxed

boxed:翻译过来是盒子被包装的意思。即:返回由IntStream流中的元素组成的Stream流,每个box 都装着Integer类
在Stream流中,是没有boxed()方法的;
只有在IntStream、DoubleStream、LongStream 这三种类型的流中,才有boxed()方法

源码:

@Override
public final Stream<Integer> boxed() {
    return mapToObj(Integer::valueOf);
}

解析:

①IntStream流中存的是 int 类型的 Stream,而Steam<Integer>是一个存了Integer的Stream。
看源码发现,boxed()的作用就是将int类型的Stream转成了Integer类型的Stream
②即:将IntStream转换成Stream<Integer>的过程,该过程也可通过上面介绍到的mapToObj()方法来实现

@Test
public void test17() {
    // boxed:返回一个流中元素的包装类的流(即:将IntStream转成Stream<Integer>类型)

    // 1.通过mapToObj()方法的方式返回Stream<xxx>类型
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);
    intStream.mapToObj(Integer::valueOf).collect(Collectors.toList()).forEach(System.out::println);

    // 2.通过boxed()方法的方式返回Stream<Integer>类型
    IntStream intStream1 = IntStream.of(1, 9, 4, 35, 5, 2);
    Stream<Integer> boxed = intStream1.boxed();  // 看这里,通过boxed返回的是Stream<Integer>类型
    List<Integer> collect = boxed.collect(Collectors.toList()); // 转换成List<Integer>类型
}

扩展:

@Test
public void test18() {
    // IntStream 转 Stream<Integer>
    IntStream intStream = IntStream.of(111, 222);
    Stream<Integer> boxed = intStream.boxed();
    // Stream<Integer> 转 IntStream
    Stream<Integer> integerStream = Stream.of(1111, 222);
    IntStream intStream1 = integerStream.mapToInt(Integer::valueOf);
}

19.toArray

toArray:将IntStream流中的元素转换成一个数组

@Test
public void test19() {
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);
    // toArray:将IntStream流中的元素转换成一个数组
    int[] intArray = intStream.toArray();
    System.out.println(Arrays.toString(intArray));
}

输出结果:

[1, 9, 4, 35, 5, 2]

20.close / onClose

close:用于关闭Stream流,
onClose:该方法用于注册一个回调函数它返回的是一个新的流,可以连续调用onClose,注册多个回调方法

@Test
public void test21() {
    // IntStream流
    IntStream intStream = IntStream.of(1, 9, 4, 35, 5, 2);

    // 使用onClose()方法注册多个回调函数
    IntStream newStream = intStream
                            .onClose(() -> System.out.println("1"))
                            .onClose(() -> System.out.println("2"))
                            .onClose(() -> System.out.println("3"));

    // 调用close()方法关闭流时,会一次执行注册的回调函数
    newStream.close();

    // 关闭流后,就不能再使用该流了(此处会报错:stream has already been operated upon or closed)
    int sum = newStream.sum();

}

输出结果:

// 关闭流时的回调函数内容
1
2
3
// 报错信息
java.lang.IllegalStateException: stream has already been operated upon or closed

21.collect

collect:用于将流中最终计算好的数据进行收集,并返回

collect() 方法的使用,也有很多内容学习,此处内容过多,不做一一列举。
在使用collect()方法时,我们会常用到一个Collectors工具类,该工具类为我们提供了很多日常开发中会用到的一些方法

比如说:
1.将结果收集到List集合
2.将结果收集到Set集合
3.将结果收集到Collection集合中
4.将结果进行聚合计算后返回
… … 等很多工具类,挺好用的。

此处不过多介绍。请参考:JDK8辅助学习(四):Stream流 collect() 方法的详细使用介绍

Example:此处来一个简单的案例,将strem流计算返回的结果,收集到List集合中

@Test
public void test23() {
    // IntStream流
    IntStream intStream = IntStream.of(2, 9, 12, 35, 38, 8, 13, 39, 58, 86, 63);

    // 操作
    List<Integer> collect = intStream.filter(val -> val > 10)  // 过滤掉<10的,返回:12, 35, 38, 13, 39, 58, 86, 63
            .map(val -> 2 * val)   // 翻倍,返回:24, 70, 76, 26, 78, 116, 172, 126
            .skip(2)  // 跳过前2个,返回:76, 26, 78, 116, 172, 126
            .limit(4)  // 截取前4个,返回:76, 26, 78, 116
            .boxed()  // IntStream 转 Stream<Integer>
            .collect(Collectors.toList());  // 将结果收集至List集合

    // 输出
    collect.forEach(System.out::println);
}

输出结果:

76
26
78
116

Collectors工具类的使用,更多内容请参考:JDK8辅助学习(四):Stream流 collect() 方法的详细使用介绍


博主写作不易,加个关注呗

求关注、求点赞,加个关注不迷路 ヾ(◍°∇°◍)ノ゙

我不能保证所写的内容都正确,但是可以保证不复制、不粘贴。保证每一句话、每一行代码都是亲手敲过的,错误也请指出,望轻喷 Thanks♪(・ω・)ノ