java lambda表达式与Stream

2 篇文章 0 订阅
1 篇文章 0 订阅

本文简单介绍了关于Java lambda表达式及Stream操作的相关知识,包括基本概念、引入原因、简单使用方法等,供大家参考讨论。

引入版本

lambda表达式及Stream均在Java8中引入,所以开发时需要保证环境配置正确,如在IDEA上,必须保证三个地方都配置为java8以上:

  • File –> Project Stucture –> Project 下的所有版本配置
  • File –> Project Stucture –> Modules –> Sources –> Lanugage level
  • File –> Settings –> Compiler –> Java Compiler

lambda表达式

引入原因

Java8有一个短期目标和一个长期目标。

  • 短期目标是:配合“集合类批处理操作”的内部迭代和并行处理,集合类的批处理操作API的目的是实现集合类的“内部迭代”,并期望充分利用现代多核CPU进行并行计算。;
  • 长期目标是:将Java向函数式编程语言这个方向引导(并不是要完全变成一门函数式编程语言,只是让它有更多的函数式编程语言的特性),也正是由于这个原因,Oracle并没有简单地使用内部类去实现lambda表达式,而是使用了一种更动态、更灵活、易于将来扩展和改变的策略。

概念

lambda表达式

“lambda表达式”(λ表达式,lambda expression):

  • 是一个匿名函数(即,没有函数名的函数);
  • 一个lambda 表达式其实就是定义了一个匿名方法,只不过这个方法必须符合至少一个函数接口;
  • lambda 表达式主要用于替换以前广泛使用的内部匿名类,各种回调,比如事件响应器、传入Thread类的Runnable等。

函数接口

函数接口(Java8新引入的概念):一个接口,如果只有一个显式声明的抽象方法,那么它就是一个函数接口。

方法引用

方法引用(Method reference):任何一个lambda表达式都可以代表某个函数接口的唯一方法的匿名描述符。我们也可以使用某个类的某个具体方法来代表这个描述符,叫做方法引用。

例如:

  • Integer::parseInt //静态方法引用
  • System.out::print //实例方法引用
  • Person::new //构造器引用

下面是一组例子,教你使用方法引用代替λ表达式:

    //c1 与 c2 是一样的(静态方法引用)
    Comparator<Integer> c2 = (x, y) -> Integer.compare(x, y);
    Comparator<Integer> c1 = Integer::compare;

    //下面两句是一样的(实例方法引用1)
    persons.forEach(e -> System.out.println(e));
    persons.forEach(System.out::println);

    //下面两句是一样的(实例方法引用2)
    persons.forEach(person -> person.eat());
    persons.forEach(Person::eat);

    //下面两句是一样的(构造器引用)
    strList.stream().map(s -> new Integer(s));
    strList.stream().map(Integer::new);

扩展

Stream

引入原因

Java 8 中的 Stream 是对集合(Collection)对象功能的增强。

特性

Stream 的特性可以归纳为:

  • 不是数据结构,它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据。
  • 它也绝不修改自己所封装的底层数据结构的数据,例如 Stream 的 filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。
  • 所有 Stream 的操作必须以 lambda 表达式为参数
  • 不支持索引访问,你可以请求第一个元素,但无法请求第二个,第三个,或最后一个。
  • 很容易生成数组或者 List
  • 惰性化,很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。Intermediate 操作永远是惰性化的。
  • 并行能力,当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。
  • 可以是无限的,集合有固定大小,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting 操作可以对无限的 Stream 进行运算并很快完成。

性能评估

  • 对于简单操作,比如最简单的遍历,Stream串行API性能明显差于显示迭代,但并行的Stream API能够发挥多核特性。
  • 对于复杂操作,Stream串行API性能可以和手动实现的效果匹敌,在并行执行时Stream API效果远超手动实现。
  • 还有一个方法叫parallelStream(),顾名思义它和stream()一样,只不过指明要并行处理,以期充分利用现代CPU的多核特性。

适应场景

如果出于性能考虑:

  • 对于简单操作推荐使用外部迭代手动实现,
  • 对于复杂操作,推荐使用Stream API,
  • 在多核情况下,推荐使用并行Stream API来发挥多核优势,
  • 单核情况下不建议使用并行Stream API。

如果出于代码简洁性考虑:
使用Stream API能够写出更短的代码。即使是从性能方面说,尽可能的使用Stream API也具有另外一个优势,那就是只要Java Stream类库做了升级优化,代码不用做任何修改就能享受到升级带来的好处。

扩展

应用实例

典型的大数据处理方法,Filter-Map-Reduce:

    //给出一个String类型的数组,找出其中所有不重复的素数
    public void distinctPrimary(String... numbers) {
        List<String> l = Arrays.asList(numbers);
        List<Integer> r = l.stream()
                .map(e -> new Integer(e))
                .filter(e -> Primes.isPrime(e))
                .distinct()
                .collect(Collectors.toList());
        System.out.println("distinctPrimary result is: " + r);
    }
  • 第一步:传入一系列String(假设都是合法的数字),转成一个List,然后调用stream()方法生成流。
  • 第二步:调用流的map方法把每个元素由String转成Integer,得到一个新的流。map方法接受一个Function类型的参数,上面介绍了,Function是个函数接口,所以这里用λ表达式。
  • 第三步:调用流的filter方法,过滤那些不是素数的数字,并得到一个新流。filter方法接受一个Predicate类型的参数,上面介绍了,Predicate是个函数接口,所以这里用λ表达式。
  • 第四步:调用流的distinct方法,去掉重复,并得到一个新流。这本质上是另一个filter操作。
  • 第五步:用collect方法将最终结果收集到一个List里面去。collect方法接受一个Collector类型的参数,这个参数指明如何收集最终结果。在这个例子中,结果简单地收集到一个List中。我们也可以用Collectors.toMap(e->e, e->e)把结果收集到一个Map中,它的意思是:把结果收到一个Map,用这些素数自身既作为键又作为值。toMap方法接受两个Function类型的参数,分别用以生成键和值,Function是个函数接口,所以这里都用λ表达式。

常用的收集器方法,groupingBy:

    //给出一个String类型的数组,找出其中各个素数,并统计其出现次数
    public void primaryOccurrence(String... numbers) {
        List<String> l = Arrays.asList(numbers);
        Map<Integer, Integer> r = l.stream()
            .map(e -> new Integer(e))
            .filter(e -> Primes.isPrime(e))
            .collect( Collectors.groupingBy(p->p, Collectors.summingInt(p->1)) );
        System.out.println("primaryOccurrence result is: " + r);
    }

注意这一行:Collectors.groupingBy(p->p, Collectors.summingInt(p->1))它的意思是:把结果收集到一个Map中,用统计到的各个素数自身作为键,其出现次数作为值。

==================================
==疑问?帮助?批评?欢迎评论 | QQ:593159978==
==================================

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值