Java8新特性学习(三)- Stream类

Java8新特性学习(三)- Stream类

背景及介绍

这里提到Java8的Stream类并不像Java以前版本的InputStream和OutputStream,他们是几乎不搭边的两个类。Stream类常跟集合处理一起使用,算是集合的一个增强。Stream类比前面提到的Optional类更加通用,原因在于Stream类提供更丰富的API,满足了程序中通用的集合处理方法,且使用lambda方便,较Java7减少较多的代码。

举例说明

使用Stream和lambda编码,可以提高代码效率,对于过滤取值这种常规操作,相比起java7来实现高效简单。下面举例说明,假设我们有一组求职者的简历集合,我们需要从中过滤具有3年以上工作经验,且有高并发技能的Java开发工程师的求职者,得到他们的手机号,进行面试邀约。

    public class Resume {
        private String name;
        private Integer workingAge;
        private List<String> skillList;
        private String job;
        private String phone;
    }
		//java7
        List<String> phoneList = new ArrayList<>();
        for (Resume resume : resumeList) {
            if (resume.getWorkingAge() < 3) {
                continue;
            }
            if (!resume.getSkillList().contains("高并发")) {
                continue;
            }
            if (!"Java".equals(resume.getJob())) {
                continue;
            }
            phoneList.add(resume.getPhone());
        }
		//java8
        List<String> phoneList = resumeList.stream()
                .filter(resume -> resume.getWorkingAge() >= 3)
                .filter(resume -> resume.getSkillList().contains("高并发"))
                .filter(resume -> "Java".equals(resume.getJob()))
                .map(Resume::getPhone).collect(Collectors.toList());

重要方法介绍

java.util.Collection#stream#parallelStream

集合类对象可以直接通过stream()/paralleStream()方法创建串行流和并行流。如

        List<String> phoneList = resumeList.stream()
                .map(Resume::getPhone).collect(Collectors.toList());

of\generate

of()用来显式创建串行的Stream对象,有两个重载方法,一个是元素个数不限,另一个只能传一个值。基础数据类型的流,可以扩展使用IntStream、LongStream、DoubleStream.

特别说明:流只能使用一次,不然会抛异常java.lang.IllegalStateException: stream has already been operated upon or closed.

		//of()
        Stream<Integer> integerStream = Stream.of(1,2,3,4,5,6);
        integerStream.filter(i -> i > 4).forEach(System.out::println);

这里创建的是串行的运行方式,如果想要创建并行的,可以通过Colletion#parallelStream()方法创建。of()方法暂时没有提供这个特性。

generate()方法可以自己生成控制Stream,这个方法适合用来创建常量的Stream或者随机元素的Stream。generate()是无限生成流的,需要配合limit()来使用.下面举例在1000个数字中随机抽取5个幸运中奖号码.

		//generate()
Stream.generate(() -> new Random().nextInt(1000)).limit(5).forEach(System.out::println);

filter

filter会对Stream中的所有元素进行过滤,会创建一个新的Stream,其元素均符合过滤条件。如文章开头举例的简历过滤:

		//java8
        List<String> phoneList = resumeList.stream()
                .filter(resume -> resume.getWorkingAge() >= 3)
                .filter(resume -> resume.getSkillList().contains("高并发"))
                .filter(resume -> "Java".equals(resume.getJob()))
                .map(Resume::getPhone).collect(Collectors.toList());

map/flatMap

map和flatMap都可以被用在一个Stream对象上,且都能返回Stream.不同之处在于map操作会对每一个输入返回一个结果,然而flatMap操作会返回任意个(0个/1个/多个)值.这取决于在每个方法上的操作。

map操作接受一个Function,会将传入的每个值都调用改Function,然后得到结果后返回。而flatMap操作会对每个参数产生任意数量结果。在Java编程中,返回任意结果可能会在处理时比较麻烦,需要考虑更多的情况,因为返回值可能是一个值,Array或List。我们可以把flatMap操作可以看成是map操作 + flat操作,首先应用Function得到结果,然后将结果flat出来。而map操作相比flatMap操作则可以视为没有最后的flat操作。下面举例:

    @Test
    public void testFlatMap() {
        List<Integer> intList1 = new ArrayList<>();
        intList1.add(1);
        intList1.add(2);
        List<Integer> intList2 = new ArrayList<>();
        intList1.add(3);
        intList1.add(4);

        List<Integer> list = Stream.of(intList1, intList2).flatMap(List::stream).collect(Collectors.toList());
        System.out.println(list);
        //[1, 2, 3, 4]
    }
    

再举例,现在有几个单词,想过滤单词中不重复的字母。map操作无法得到,而flatMap可以得到。

        //map
        List<Stream<String>> sMap = Arrays.stream(words).map(word -> word.split(""))
                .map(Arrays::stream).distinct()
                .collect(Collectors.toList());

        //flatMap
        List<String> sFlatMap = Arrays.stream(words).map(word -> word.split(""))
                .flatMap(Arrays::stream).distinct()
                .collect(Collectors.toList());

        System.out.println(sFlatMap);
        //[J, A, V, G, O]

Stream map(Function<? super T, ? extends R> mapper);

Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

collect

collect方法用于将Stream流中的对象转化还原为流的结果。如

    List<String> asList = stringStream.collect(Collectors.toList());
    
    Map<String, List<Person>> peopleByCity   = personStream.collect(Collectors.groupingBy(Person::getCity));
    
    Map<String, Map<String, List<Person>>> peopleByStateAndCity    = personStream.collect(Collectors.groupingBy(Person::getState,                                                       Collectors.groupingBy(Person::getCity)));

anyMatch\allMatch

anyMatch:检测是否流中是否有任一元素满足提供Predicate.

allMatch:检测是否流中是否所有元素满足提供Predicate.

        boolean anyMatch = Stream.of(1, 2, 3, 4, 5, 6).anyMatch(i -> i > 6);
        System.out.println(anyMatch);//false
        boolean allMatch = Stream.of(1, 2, 3, 4, 5, 6).allMatch(i -> i < 20);
        System.out.println(allMatch);//true

min\max

根据参数Comparator返回流中最大\小的元素,返回类型是Optional。更多Optional信息,请查看
Java8新特性学习(二)- Optional类

        Optional<Integer> optionalMin = Stream.of(1, 2, 3, 4, 5, 6).min((a, b) -> a > b ? 1 : -1);
        System.out.println(optionalMin.orElse(null));//1

        Optional<Integer> optionalMax = Stream.of(1, 2, 3, 4, 5, 6).max(Integer::compareTo);
        System.out.println(optionalMax.orElse(null));//6

skip\limit

skip:返回除去前n个元素之外剩余元素组成的流。如果全部被抛弃,则返回一个空流。

limit:返回最大元素个数不超过n的流

        Stream.of(1,2,3,4,5,6).skip(20l).forEach(System.out::print);
        Stream.of(1,2,3,4,5,6).limit(3l).forEach(System.out::print);

reduce

reduce是关联累积函数对流的元素进行缩减,并返回缩减之后的Optional类型的结果。

reduce()提供了3种方式使用

1、Optional<T> reduce(BinaryOperator<T> accumulator);//返回值是Optional

2、T reduce(T identity, BinaryOperator<T> accumulator);//返回值是T,identity一样的类型

3<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);
                 //返回值是U,identity一样的类型,accumulator累加器,combiner:结合器

举例使用

	System.out.println(Stream.of(1,2,3,4,5,6).reduce( (a, b) -> a+b).orElse(0));
	System.out.println(Stream.of(1,2,3,4,5,6).reduce(0, (a, b) -> a+b));

这里会对Stream中的元素遍历每一个,进行BinaryOperator的apply操作,并最终返回一个类似合并各元素之后的结果。而我们常用的sum,max,min等操作都是一种特殊的reduce操作。可以看IntStream中min()函数的实现:

    public final OptionalInt min() {
        return reduce(Math::min);
    }

总结

  • Stream不是一种数据结构
  • Stream并不会自己存储数据,而是利用原数据进行操作
  • Stream只能使用一次,使用多次会抛出异常
  • 具有串行和并行能力,简单高效就能写出高并发代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值