Java8 Stream API 之 IntStream 用法全解

  目录

1、创建int流

2、filter / map / flatMap /peek

3、mapToObj / mapToLong / mapToDouble / asLongStream / asDoubleStream

4、forEach / forEachOrdered

5、 reduce / collect

6、distinct / sorted / limit / skip

7、sum / min / max / count / average / summaryStatistics

8、anyMatch / allMatch / noneMatch

9、findFirst / findAny

10、sequential / parallel

11、iterator / boxed / toArray

12、close / onClose


       Java8支持的流处理的元素类型只有4种,double、int,long和reference类型,可参考AbstractPipeline和BaseStream的类继承关系,如下:

Java8 Stream API 之 IntStream 用法全解_第1张图片

Java8 Stream API 之 IntStream 用法全解_第2张图片 

AbstractPipeline有4个子类,分别表示流处理元素为double、int,long和reference类型的管道;这4个子类都是抽象类,每个子类下都有3个静态内部类的实现类,Head、StatefulOp、StatelessOp,其中Head用于创建一个全新的流,StatefulOp表示有状态的一类操作,StatelessOp表示无状态的一类操作,这里的有状态是指前面流元素的处理会直接影响后面流元素的处理,多线程并行处理下每次运行的结果都不相同。这4个AbstractPipeline子类的用法和实现是基本一致的,我们以IntStream为例来说明其方法使用与实现细节,本篇博客重点说明其用法。该类的类继承关系如下:

Java8 Stream API 之 IntStream 用法全解_第3张图片

其中IntStream接口定义了int流支持的所有操作。 

1、创建int流

      创建int流都是由IntStream接口类中的静态方法完成的,具体如下:

  • of / builder: 可指定int流中包含的具体单个元素
  • range / rangeClosed : 将指定范围内的元素都添加到int流中,前者不包含最后一个元素,后者包含
  • generate / iterate :  指定生成int流中int元素的生成函数,前者的生成函数没有入参,后者会将前一次调用结果作为下一次调用生成函数的入参
  • concat :将两个int流合并成一个

各方法的使用细节可参考如下测试用例:

 @Test
    public void test() throws Exception {
        //包含指定的元素
//        IntStream intStream=IntStream.of(1);
        //返回的int流中的元素是已经排序好的
        IntStream intStream=IntStream.of(1,3,2,5,4,6);
        print("of",intStream);

        //从11到16,不包含16
        intStream=IntStream.range(11,16);
        //从11到16,包含16
//        intStream=IntStream.rangeClosed(11,16);
        print("range",intStream);

        //包含指定的元素,add方法底层也是调用accept方法,然后返回this
        //返回的int流中的元素顺序与添加顺序一致
        intStream=IntStream.builder().add(23).add(22).add(21).build();
        print("builder", intStream);

        //指定一个int生成函数
        //返回的int流中的元素不排序
        intStream=IntStream.generate(()->{
            Random random=new Random();
            return random.nextInt(100);
        }).limit(6);
        print("generate", intStream);

        //指定一个int生成函数,前一次执行函数的结果会作为下一次调用函数的入参
        //第一个参数seed就是第一次调用生成函数的入参
        //返回的int流中的元素不排序
        intStream=IntStream.iterate(1,x->{
           int a=2*x;
           if(a>16){
               return a-20;
           }else{
               return a;
           }
        }).limit(6);
        print("iterate", intStream);
    }

    @Test
    public void test2() throws Exception {
        IntStream streamA=IntStream.range(11,15);
        IntStream streamB=IntStream.range(6,10);
        //将两个IntStream 合并起来
        //返回的int流的元素顺序与添加的流的元素顺序一致,不排序
        IntStream streamC=IntStream.concat(streamA,streamB);
        print("concat", streamC);
    }

    private void print(String start, IntStream intStream){
        System.out.println("print for->"+start);
        intStream.forEach(x->{
            System.out.println(x);
        });
    }

 上述方法的底层实现最终都是调用StreamSupport.intStream(Spliterator.OfInt spliterator, boolean parallel)方法,如下图:

Java8 Stream API 之 IntStream 用法全解_第4张图片

Java8 Stream API 之 IntStream 用法全解_第5张图片

2、filter / map / flatMap /peek

      filter方法会将filter函数返回false的元素从流中去除,只保留filter函数返回true的元素;map方法用于对流中的所有元素执行某个修改动作;peek方法通常用于打印流中的元素,peek函数无返回值;flatMap方法同map方法,区别在于flatMap函数的返回值是一个IntStream 而非int值,可以在返回的IntStream中包含多个元素,flatMap方法最终返回的IntStream是将每次调用flatMap函数返回的IntStream 合并后的结果。其测试用例如下:

  @Test
    public void test3() throws Exception {
        IntStream intStream=IntStream.rangeClosed(1, 10);
        //会保留过滤函数返回true的元素,此处是保留偶数
        intStream=intStream.filter(x->{
           return x%2==0;
        }).peek(x->{ //peek方法指定的函数,以流中的元素为入参,无返回值,即不会修改元素本身
            System.out.println("filter->"+x);
        });
        //对流中的所有元素执行某个修改动作,此处是将所有值加1
        intStream=intStream.map(x->{
            return x+1;
        }).peek(x->{
            System.out.println("map->"+x);
        });

        //flatMap同map,区别在于flatMap指定的函数其返回值是一个IntStream,而非一个int值,最终flatMap返回的
        //IntStream是将每次调用flatMap返回的子IntStream合并后的结果
        intStream=intStream.flatMap(x->{
            //返回IntStream时可以返回多个元素
            return IntStream.of(x+3,x+2,x+1);
        }).peek(x->{
            System.out.println("flatMap->"+x);
        });

        print("after flatMap", intStream);
    }

其输出如下:

print for->after flatMap
filter->2
map->3
flatMap->6
6
flatMap->5
5
flatMap->4
4
filter->4
map->5
flatMap->8
8
flatMap->7
7
flatMap->6
6
filter->6
map->7
flatMap->10
10
flatMap->9
9
flatMap->8
8
filter->8
map->9
flatMap->12
12
flatMap->11
11
flatMap->10
10
filter->10
map->11
flatMap->14
14
flatMap->13
13
flatMap->12
12

 其中因为flatMap函数返回的IntStream中包含了3个元素,所以peek方法执行了3次,每次都打印三个flatMap。从上述输出可知,只有开始执行forEach方法后才真正执行流处理动作,且原始的intStream只遍历一遍,每次遍历一个元素就按照代码声明的顺序依次执行各个处理动作。

3、mapToObj / mapToLong / mapToDouble / asLongStream / asDoubleStream

      这几个方法都是将int流转换成其他类型的流,mapTo的三个方法可以指定具体的转换函数,as的两个方法不能指定,遵循标准的int到指定类型的转换规则,其实现如下:

 @Test
    public void test4() throws Exception {
        IntStream intStream=IntStream.rangeClosed(1, 3);
        intStream.mapToObj(x->{
            return new Age(x);
        }).forEach(x->{
            System.out.println("mapToObj->"+x);
        });

        //执行完mapToObj后intStream本身已经关闭了不能继续操作,只能操作其返回的新流
        //此处要继续操作就必须重新初始化一个新的IntStream
        intStream=IntStream.rangeClosed(1, 3);
        intStream.mapToLong(x->{
            return x+1;
        }).forEach(x->{
            System.out.println("mapToLong->"+x);
        });

        intStream=IntStream.rangeClosed(1, 3);
        intStream.mapToDouble(x->{
            return x+2;
        }).forEach(x->{
            System.out.println("mapToDouble->"+x);
        });

        //同上面的mapToLong,区别在于不能指定转换函数,而是采用标准的int到long类型的转换方法
        intStream=IntStream.rangeClosed(1, 3);
        intStream.asLongStream().forEach(x->{
            System.out.println("asLongStream->"+x);
        });

        intStream=IntStream.rangeClosed(1, 3);
        intStream.asDoubleStream().forEach(x->{
            System.out.println("asDoubleStream->"+x);
        });
    }

其输出如下:

Java8 Stream API 之 IntStream 用法全解_第6张图片

4、forEach / forEachOrdered

      这两个方法都是用来遍历流中的元素,跟peek方法不同的是该这两个方法的返回值是void,执行此类返回值为void的方法会触发实际的流处理动作。forEachOrdered同forEach的区别在于并行流处理下,forEachOrdered会保证实际的处理顺序与流中元素的顺序一致,而forEach方法无法保证,默认的串行流处理下,两者无区别,都能保证处理顺序与流中元素顺序一致,测试用例如下:

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

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

  其输出如下:

Java8 Stream API 之 IntStream 用法全解_第7张图片

5、 reduce / collect

       reduce方法用于执行类似于累加的操作,上一次调用处理函数的结果会作为入参下一次调用的入参;collect方法的效果跟forEach类似,注意其返回值是调用supplier函数的返回值,该函数在整个流处理过程中只调用一次,参考如下测试用例:

@Test
    public void test7() throws Exception {
        IntStream intStream=IntStream.of(6,1,3,2,5,4);
        OptionalInt optionalInt=intStream.reduce((x, y)->{
            System.out.println("x->"+x+",y->"+y);
            return x+y;
        });
        System.out.println("result->"+optionalInt.getAsInt());

        System.out.println("");

        intStream=IntStream.of(6,1,3,2,5,4);
        //同第一个reduce方法,区别在于可以指定起始的left,第一个reduce方法使用第一个元素作为起始的left
        int result=intStream.reduce(2,(x, y)->{
            System.out.println("x->"+x+",y->"+y);
            return x+y;
        });
        System.out.println("result->"+result+"\n");

        intStream=IntStream.of(6,1,3,2,5,4);
        //同forEach方法,首先调用supplier函数生成一个值,将该值作为accumulator函数的第一个参数,accumulator函数的第二个
        //参数就是流中的元素,注意第三个参数combiner无意义,可置为null
        result=intStream.collect(()->{
            Random random=new Random();
            return random.nextInt(10);
        },(x,y)->{
            System.out.println("ObjIntConsumer x->"+x+",y->"+y);
        },null);
        //返回值是supplier函数生成的值
        System.out.println("collect result->"+result+"\n");

    }

其输出如下:

Java8 Stream API 之 IntStream 用法全解_第8张图片

6、distinct / sorted / limit / skip

      distinct用于对流中的元素去重,sorted用于对流中的元素升序排序,limit用于限制流中元素的个数,多余的元素会被丢弃,skip用于跳过前面指定N个元素,参考如下测试用例:

  @Test
    public void test8() throws Exception {
        IntStream intStream=IntStream.of(6,1,1,2,5,2,3,3,4,8,6,11,10,9);
        intStream.distinct() //对流中的元素去重
                 .sorted()  //将流中的元素排序,默认升序
                 .skip(3) //跳过前3个元素,此处是跳过1,2,3三个元素
                 .limit(6) //限制流中元素的最大个数
                 .forEach(x->{
                     System.out.println(x);
                 });

    }

 其输出如下:

Java8 Stream API 之 IntStream 用法全解_第9张图片

7、sum / min / max / count / average / summaryStatistics

     前面5个方法分别是获取流中元素的总和,最小值,最大值,个数和平均值,最后一个summaryStatistics方法是一次调用获取上述属性。测试用例如下:

@Test
    public void test9() throws Exception {
        IntStream intStream=IntStream.of(6,1,1,2,5,2,3,4);
        //取流中元素的最大值
        OptionalInt max=intStream.max();
        System.out.println("max->"+max.getAsInt());

        //同其他没有流的方法,max操作会中断流,再对该流执行相关流处理方法会报错synchronizedTest.StreamTest
        intStream=IntStream.of(6,1,1,2,5,2,3,4);
        //取流中元素的最小值
        OptionalInt min=intStream.min();
        System.out.println("max->"+max.getAsInt());

        intStream=IntStream.of(6,1,1,2,5,2,3,4);
        //取流中元素的平均值
        OptionalDouble average=intStream.average();
        System.out.println("average->"+average.getAsDouble());

        intStream=IntStream.of(6,1,1,2,5,2,3,4);
        //取流中元素的个数
        long count=intStream.count();
        System.out.println("count->"+count);

        intStream=IntStream.of(6,1,1,2,5,2,3,4);
        //取流中元素的总和
        int sum=intStream.sum();
        System.out.println("sum->"+sum);

        intStream=IntStream.of(6,1,1,2,5,2,3,4);
        //取流中元素的统计情形,即一次返回min,max,count等属性
        IntSummaryStatistics summaryStatistics=intStream.summaryStatistics();
        System.out.println(summaryStatistics.toString());
    }

 其输出如下:

Java8 Stream API 之 IntStream 用法全解_第10张图片

8、anyMatch / allMatch / noneMatch

     有任何一个元素匹配,anyMatch返回true;所有元素都匹配时,allMatch返回true;没有一个元素匹配时,noneMatch返回true,参考如下测试用例:

@Test
    public void test10() throws Exception {
        IntStream intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
        //有任何一个匹配,返回true
        boolean anyMatch=intStream.anyMatch(x->{
            return x%2==0;
        });
        System.out.println("anyMatch->"+anyMatch);

        intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
        //所有的都匹配,返回true
        boolean allMatch=intStream.allMatch(x->{
            return x%2==0;
        });
        System.out.println("allMatch->"+allMatch);

        intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
        //所有的都不匹配,返回true
        boolean noneMatch=intStream.noneMatch(x->{
            return x%2==0;
        });
        System.out.println("noneMatch->"+noneMatch);
    }

 输出如下:

Java8 Stream API 之 IntStream 用法全解_第11张图片

9、findFirst / findAny

      findFirst返回流中第一个元素,findAny返回流中的任意一个元素,参考如下用例:

@Test
    public void test11() throws Exception {
        IntStream intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
        //返回第一个元素
        OptionalInt result=intStream.findFirst();
        System.out.println("findFirst->"+result.getAsInt());

        for(int i=0;i<6;i++) {
            intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
            //返回任意一个元素
            result = intStream.findAny();
            System.out.println("findAny->" + result.getAsInt());
        }
    }

10、sequential / parallel

       sequential 返回的流是串行处理,parallel返回的流是并行处理,参考如下测试用例:

@Test
    public void test12() throws Exception {
        IntStream intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
        long start=System.currentTimeMillis();
        //并行处理
        intStream.parallel().forEach(x->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":"+x);
        });
        System.out.println("parallel time->"+(System.currentTimeMillis()-start));

        intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
        start=System.currentTimeMillis();
        //默认都是串行处理
        intStream.sequential().forEach(x->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":"+x);
        });
        System.out.println("sequential time->"+(System.currentTimeMillis()-start));
    }

 其输出如下:

Java8 Stream API 之 IntStream 用法全解_第12张图片

从输出可知parallel返回的流起了4个线程来处理,主线程和3个commonPool线程,这4个线程并不是严格的从一开始就并行执行,因为其执行总耗时是2061ms,而不是1s左右;串行处理是各个元素的处理耗时累加起来,8个元素,刚好是8s。 

11、iterator / boxed / toArray

      iterator 返回一个元素遍历器实现,boxed 返回一个流中元素的包装类的流,可用mapToObj实现相同的功能,toArray将流中的元素作为一个数组返回,参考如下测试用例:

@Test
    public void test13() throws Exception {
        IntStream intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);

        //返回一个包装类的流,效果相当于下面的mapToObj方法
//        Stream integerStream=intStream.boxed();
        Stream integerStream=intStream.mapToObj(x->{
            return new Integer(x);
        });
        integerStream.forEach(x->{
            System.out.println("boxed->"+x);
        });

        //返回一个元素遍历器,PrimitiveIterator是继承自Iterator
        intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
        PrimitiveIterator.OfInt iterator=intStream.iterator();
        while (iterator.hasNext()){
            System.out.println("iterator->"+iterator.next());
        }

        //将流中的元素转换成一个数组
        intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
        int[] results=intStream.toArray();
        System.out.println("toArray->"+Arrays.toString(results));
    }

其输出如下:

Java8 Stream API 之 IntStream 用法全解_第13张图片

12、close / onClose

       close方法会关闭流,并触发所有的onClose方法的执行;onClose方法用于注册一个回调函数,该方法返回一个新的流,可以连续调用注册多个回调函数,close触发时会按照注册的顺序依次执行。参考如下测试用例:

@Test
    public void test14() throws Exception {
        IntStream intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);

        //onClose方法的返回值是一个新的流,可以连续调用onClose,注册多个回调方法
        intStream.onClose(()->{
            System.out.println("intStream isClosed one ");
        }).onClose(()->{
            System.out.println("intStream isClosed two");
        }).onClose(()->{
            System.out.println("intStream isClosed three");
        });

        //触发onClose方法注册的多个回调方法的执行,并关闭流
        intStream.close();
        //流已关闭,不能执行流处理动作,forEach执行完成也会关闭流但是不会触发onClose方法的执行
//        intStream.forEach(x->{
//            System.out.println(x);
//        });
        System.out.println("main end");
    }

其输出如下:

Java8 Stream API 之 IntStream 用法全解_第14张图片

 

你可能感兴趣的:(java8并发工具类源码解析)