Java8新特性之lambda的作用_动力节点Java学院整理

我们期待了很久lambda为java带来闭包的概念,但是如果我们不在集合中使用它的话,就损失了很大价值。现有接口迁移成为lambda风格的问题已经通过default methods解决了,在这篇文章将深入解析Java集合里面的批量数据操作(bulk operation),解开lambda最强作用的神秘面纱。

1.关于JSR335

JSR是Java Specification Requests的缩写,意思是Java 规范请求,Java 8 版本的主要改进是 Lambda 项目(JSR 335),其目的是使 Java 更易于为多核处理器编写代码。

2.外部VS内部迭代

以前Java集合是不能够表达内部迭代的,而只提供了一种外部迭代的方式,也就是for或者while循环。

List persons = asList(new Person("Joe"), new Person("Jim"), new Person("John"));
for (Person p : persons) {
 p.setLastName("Doe");
}

上面的例子是我们以前的做法,也就是所谓的外部迭代,循环是固定的顺序循环。在现在多核的时代,如果我们想并行循环,不得不修改以上代码。效率能有多大提升还说定,且会带来一定的风险(线程安全问题等等)。

要描述内部迭代,我们需要用到Lambda这样的类库,下面利用lambda和Collection.forEach重写上面的循环

persons.forEach(p->p.setLastName("Doe"));

现在是由jdk 库来控制循环了,我们不需要关心last name是怎么被设置到每一个person对象里面去的,库可以根据运行环境来决定怎么做,并行,乱序或者懒加载方式。这就是内部迭代,客户端将行为p.setLastName当做数据传入api里面。 内部迭代其实和集合的批量操作并没有密切的联系,借助它我们感受到语法表达上的变化。真正有意思的和批量操作相关的是新的流(stream)API。新的java.util.stream包已经添加进JDK 8了。

3.Stream API

流(Stream)仅仅代表着数据流,并没有数据结构,所以他遍历完一次之后便再也无法遍历(这点在编程时候需要注意,不像Collection,遍历多少次里面都还有数据),它的来源可以是Collection、array、io等等。

3.1中间与终点方法

流作用是提供了一种操作大数据接口,让数据操作更容易和更快。它具有过滤、映射以及减少遍历数等方法,这些方法分两种:中间方法和终端方法,“流”抽象天生就该是持续的,中间方法永远返回的是Stream,因此如果我们要获取最终结果的话,必须使用终点操作才能收集流产生的最终结果。区分这两个方法是看他的返回值,如果是Stream则是中间方法,否则是终点方法。

简单介绍下几个中间方法(filter、map)以及终点方法(collect、sum)

3.1.1Filter

在数据流中实现过滤功能是首先我们可以想到的最自然的操作了。Stream接口暴露了一个filter方法,它可以接受表示操作的Predicate实现来使用定义了过滤条件的lambda表达式。

List persons = …
Stream personsOver18 = persons.stream().filter(p -> p.getAge() > 18);//过滤18岁以上的人

3.1.2Map

假使我们现在过滤了一些数据,比如转换对象的时候。Map操作允许我们执行一个Function的实现(Function<T,R>的泛型T,R分别表示执行输入和执行结果),它接受入参并返回。首先,让我们来看看怎样以匿名内部类的方式来描述它:

Stream adult= persons
    .stream()
    .filter(p -> p.getAge() > 18)
    .map(new Function() {
     @Override
     public Adult apply(Person person) {
      return new Adult(person);//将大于18岁的人转为成年人
     }
    });

现在,把上述例子转换成使用lambda表达式的写法:

Stream map = persons.stream()
     .filter(p -> p.getAge() > 18)
     .map(person -> new Adult(person));

3.1.3Count

count方法是一个流的终点方法,可使流的结果最终统计,返回int,比如我们计算一下满足18岁的总人数

int countOfAdult=persons.stream()
      .filter(p -> p.getAge() > 18)
      .map(person -> new Adult(person))
      .count();

3.1.4Collect

collect方法也是一个流的终点方法,可收集最终的结果

List adultList= persons.stream()
      .filter(p -> p.getAge() > 18)
      .map(person -> new Adult(person))
      .collect(Collectors.toList());

或者,如果我们想使用特定的实现类来收集结果:

List adultList = persons
     .stream()
     .filter(p -> p.getAge() > 18)
     .map(person -> new Adult(person))
     .collect(Collectors.toCollection(ArrayList::new));

篇幅有限,其他的中间方法和终点方法就不一一介绍了,看了上面几个例子,大家明白这两种方法的区别即可,后面可根据需求来决定使用。

3.2顺序流与并行流

每个Stream都有两种模式:顺序执行和并行执行。

顺序流:

List <Person> people = list.getStream.collect(Collectors.toList());

并行流:

List <Person> people = list.getStream.parallel().collect(Collectors.toList());

顾名思义,当使用顺序方式去遍历时,每个item读完后再读下一个item。而使用并行去遍历时,数组会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。

3.2.1并行流原理:

List originalList = someData;
split1 = originalList(0, mid);//将数据分小部分
split2 = originalList(mid,end);
new Runnable(split1.process());//小部分执行操作
new Runnable(split2.process());
List revisedList = split1 + split2;//将结果合并

大家对hadoop有稍微了解就知道,里面的 MapReduce  本身就是用于并行处理大数据集的软件框架,其 处理大数据的核心思想就是大而化小,分配到不同机器去运行map,最终通过reduce将所有机器的结果结合起来得到一个最终结果,与MapReduce不同,Stream则是利用多核技术可将大数据通过多核并行处理,而MapReduce则可以分布式的。

3.2.2顺序与并行性能测试对比

如果是多核机器,理论上并行流则会比顺序流快上一倍,下面是测试代码

long t0 = System.nanoTime();

  //初始化一个范围100万整数流,求能被2整除的数字,toArray()是终点方法

  int a[]=IntStream.range(0, 1_000_000).filter(p -> p % 2==0).toArray();

  long t1 = System.nanoTime();

  //和上面功能一样,这里是用并行流来计算

  int b[]=IntStream.range(0, 1_000_000).parallel().filter(p -> p % 2==0).toArray();

  long t2 = System.nanoTime();

  //我本机的结果是serial: 0.06s, parallel 0.02s,证明并行流确实比顺序流快

  System.out.printf("serial: %.2fs, parallel %.2fs%n", (t1 - t0) * 1e-9, (t2 - t1) * 1e-9);

3.3关于Folk/Join框架

应用硬件的并行性在java 7就有了,那就是 java.util.concurrent 包的新增功能之一是一个 fork-join 风格的并行分解框架,同样也很强大高效,有兴趣的同学去研究,这里不详谈了,相比Stream.parallel()这种方式,我更倾向于后者。

4.总结

如果没有lambda,Stream用起来相当别扭,他会产生大量的匿名内部类,比如上面的3.1.2map例子,如果没有default method,集合框架更改势必会引起大量的改动,所以lambda+default method使得jdk库更加强大,以及灵活,Stream以及集合框架的改进便是最好的证明。

(0)

相关推荐

  • Java9的一些新特性介绍

    被接受的特性 1. Jigsaw 项目;模块化源码 Jigsaw项目是为了模块化Java代码.将JRE分成可相互协作的组件,这也是Java 9 众多特色种的一个.JEP是迈向Jigsaw四步中的第一步,它不会改变JRE和JDK的真实结构.JEP是为了模块化JDK源代码,让编译系统能够模块编译并在构建时检查模块边界.这个项目原本是随Java 8发布的,但由于推迟,所以将把它加到Java 9. 一旦它完成,它可能允许根据一个项目需求自定义组件从而减少rt.jar的大小.在JDK 7 和JDK 8的r

  • Java8新特性lambda表达式有什么用(用法实例)

    我们期待了很久lambda为java带来闭包的概念,但是如果我们不在集合中使用它的话,就损失了很大价值.现有接口迁移成为lambda风格的问题已经通过default methods解决了,在这篇文章将深入解析Java集合里面的批量数据操作(bulk operation),解开lambda最强作用的神秘面纱. 1.关于JSR335 JSR是Java Specification Requests的缩写,意思是Java 规范请求,Java 8 版本的主要改进是 Lambda 项目(JSR 335),其

  • Java8新特性之再见Permgen_动力节点Java学院整理

    很多开发者都在其系统中见过"java.lang.OutOfMemoryError: PermGen space"这一问题.这往往是由类加载器相关的内存泄漏以及新类加载器的创建导致的,通常出现于代码热部署时.相对于正式产品,该问题在开发机上出现的频率更高,在产品中最常见的"问题"是默认值太低了.常用的解决方法是将其设置为256MB或更高. PermGen space简单介绍 PermGen space的全称是Permanent Generation space,是指内

  • java新特性之for循环最全的用法总结

    1. 增强for概述 增强for循环,也叫Foreach循环,用于数组和容器(集合类)的遍历.使用foreach循环遍历数组和集合元素时,无需获得数组和集合长度,无需根据索引来访问数组元素和集合元素,大大提高的效率,代码也简洁不少. 2. Oracle官网的解释 So when should you use the for-each loop? Any time you can. It really beautifies your code. Unfortunately, you cannot

  • Java中字符串去重的特性介绍

    字符串在任何应用中都占用了大量的内存.尤其数包含独立UTF-16字符的char[]数组对JVM内存的消耗贡献最多--因为每个字符占用2位. 内存的30%被字符串消耗其实是很常见的,不仅是因为字符串是与我们互动的最好的格式,而且是由于流行的HTTP API使用了大量的字符串.使用Java 8 Update 20,我们现在可以接触到一个新特性,叫做字符串去重,该特性需要G1垃圾回收器,该垃圾回收器默认是被关闭的. 字符串去重利用了字符串内部实际是char数组,并且是final的特性,所以JVM可以任

  • Java 8 新特性终极版指南详解

    前言: Java 8已经公布有一段时间了,种种迹象表明Java 8是一个有重大改变的发行版.在Java Code Geeks上已经有很多介绍Java 8新特性的文章,例如Playing with Java 8 – Lambdas and Concurrency.Java 8 Date Time API Tutorial : LocalDateTime和Abstract Class Versus Interface in the JDK 8 Era.本文还参考了一些其他资料,例如:15 Must

  • Java语言十大基础特性分析

    Java语言的作者们编写了具有广泛影响的Java白皮书,里面详细地介绍了他们的设计目标以及实现成果,还用简短的篇幅介绍了Java语言的特性.下面将对这些特性进行介绍. 1. 简单 Java语言的语法简单明了,容易掌握,而且是纯面向对象的语言.Java语言的简单性主要体现在以下几个方面: 语法规则和C++类似.从某种意义上讲,Java语言是由C和C++语言转变而来的,所以C程序设计人员可以很容易地掌握Java语言的语法. Java语言对C++进行了简化和提高.例如,Java使用接口取代了多重继承,

  • JAVA8 十大新特性详解

    "Java is still not dead-and people are starting to figure that out." 本教程将用带注释的简单代码来描述新特性,你将看不到大片吓人的文字. 一.接口的默认方法 Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,示例如下: 复制代码 代码如下: interface Formula {    double calculate(int a); default do

  • 深入讲解Java 9中的九个新特性

    本文主要跟大家分享了Java 9中的九个新特性,对大家具有一定的参考学习价值,下面来看看详细的介绍: 一. Java 平台级模块系统 Java 9 的定义功能是一套全新的模块系统.当代码库越来越大,创建复杂,盘根错节的"意大利面条式代码"的几率呈指数级的增长.这时候就得面对两个基础的问题: 很难真正地对代码进行封装, 而系统并没有对不同部分(也就是 JAR 文件)之间的依赖关系有个明确的概念.每一个公共类都可以被类路径之下任何其它的公共类所访问到, 这样就会导致无意中使用了并不想被公开

  • Java8新特性之lambda的作用_动力节点Java学院整理

    我们期待了很久lambda为java带来闭包的概念,但是如果我们不在集合中使用它的话,就损失了很大价值.现有接口迁移成为lambda风格的问题已经通过default methods解决了,在这篇文章将深入解析Java集合里面的批量数据操作(bulk operation),解开lambda最强作用的神秘面纱. 1.关于JSR335 JSR是Java Specification Requests的缩写,意思是Java 规范请求,Java 8 版本的主要改进是 Lambda 项目(JSR 335),其

  • Java8新特性之Base64详解_动力节点Java学院整理

    BASE64 编码是一种常用的字符编码,在很多地方都会用到.但base64不是安全领域下的加密解密算法.能起到安全作用的效果很差,而且很容易破解,他核心作用应该是传输数据的正确性,有些网关或系统只能使用ASCII字符.Base64就是用来将非ASCII字符的数据转换成ASCII字符的一种方法,而且base64特别适合在http,mime协议下快速传输数据. JDK里面实现Base64的API 在JDK1.6之前,JDK核心类一直没有Base64的实现类,有人建议用Sun/Oracle JDK里面

  • Java接口的作用_动力节点Java学院整理

    1. 接口是一种规范 很好,你已经知道接口是一种规范了! 下面这张图是我们生活中遇到的接口:电源插座接口. 2. 为什么需要规范呢? 因为有了接口规范: • 任何电器只有有符合规范的插头,就可以获得电力 • 任何厂家(西门子插座,TCL插座,公牛插座...)按照规范进行制作,就能进行供电 每个厂家插座的生产技术.工艺都不一样,因为接口的implementation可以不一样,但是并不影响电器的正常工作.插座的内部实现对于电器来说是完全屏蔽的. 对于软件开发同样也是类似的: • 按照接口规范进行方

  • Java 中HashCode作用_动力节点Java学院整理

    第1 部分 hashCode的作用 Java集合中有两类,一类是List,一类是Set他们之间的区别就在于List集合中的元素师有序的,且可以重复,而Set集合中元素是无序不可重复的.对于List好处理,但是对于Set而言我们要如何来保证元素不重复呢?通过迭代来equals()是否相等.数据量小还可以接受,当我们的数据量大的时候效率可想而知(当然我们可以利用算法进行优化).比如我们向HashSet插入1000数据,难道我们真的要迭代1000次,调用1000次equals()方法吗?hashCod

  • Java8之lambda最佳实践_动力节点Java学院整理

    在8 里面Lambda是最火的主题,不仅仅是因为语法的改变,更重要的是带来了函数式编程的思想,我觉得优秀的程序员,有必要学习一下函数式编程的思想以开阔思路.所以这篇文章聊聊Lambda的应用场景,性能,也会提及下不好的一面. Java为何需要Lambda 1996年1月,Java 1.0发布了,此后计算机编程领域发生了翻天覆地的变化.商业发展需要更复杂的应用,大多数程序都跑在更强大的装备多核CPU的机器上.带有高效运行期编译器的Java虚拟机(JVM)的出现,使得程序员将精力更多放在编写干净.易

  • Serializable接口的作用_动力节点Java学院整理

    实现java.io.Serializable 接口的类是可序列化的.没有实现此接口的类将不能使它们的任一状态被序列化或逆序列化.序列化类的所有子类本身都是可序列化的.这个序列化接口没有任何方法和域,仅用于标识 序列化的语意.允许非序列化类的子类型序列化,子类型可以假定负责保存和恢复父类型的公有的.保护的和(如果可访问)包的域的状态.只要该类(扩展)有一 个无参构造子,可初始化它的状态,那么子类型就可承担上述职责.在这种情况下申明一个可序列化的类是一个错误.此错误将在运行时被检测.就是可以把对象存

  • 序列化版本号serialVersionUID的作用_动力节点Java学院整理

    Java序列化是将一个对象编码成一个字节流,反序列化将字节流编码转换成一个对象. 序列化是Java中实现持久化存储的一种方法:为数据传输提供了线路级对象表示法. Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的.在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常. Eclipse中The

  • Java数组的特性_动力节点Java学院整理

    Java中的数组是对象吗? Java和C++都是面向对象的语言.在使用这些语言的时候,我们可以直接使用标准的类库,也可以使用组合和继承等面向对象的特性构建自己的类,并且根据自己构建的类创建对象.那么,我们是不是应该考虑这样一个问题:在面向对象的语言中,数组是对象吗? 要判断数组是不是对象,那么首先明确什么是对象,也就是对象的定义.在较高的层面上,对象是根据某个类创建出来的一个实例,表示某类事物中一个具体的个体.对象具有各种属性,并且具有一些特定的行为.而在较低的层面上,站在计算机的角度,对象就是

  • Nginx简介_动力节点Java学院整理

    1.什么是Nginx Nginx来自俄罗斯的Igor Sysoev在为Rambler Media(http://www.rambler.ru/)工作期间,使用C语言开发了Nginx.Nginx作为Web服务器,一直为俄罗斯著名的门户网站Rambler Media提供着出色.稳定的服务. Igor Sysoev将Nginx的代码开源,并且赋予其最自由的2-clause BSD-like license许可证.由于Nginx使用基于事件驱动的架构能够并发处理百万级别的TCP连接,高度模块化的设计和自

  • Java Set简介_动力节点Java学院整理

    1. 概述   Java 中的Set和正好和数学上直观的集(set)的概念是相同的.Set最大的特性就是不允许在其中存放的元素是重复的.根据这个特点,我们就可以使用Set 这个接口来实现前面提到的关于商品种类的存储需求.Set 可以被用来过滤在其他集合中存放的元素,从而得到一个没有包含重复新的集合. 2. 常用方法 按照定义,Set 接口继承 Collection 接口,而且它不允许集合中存在重复项.所有原始方法都是现成的,没有引入新方法.具体的 Set 实现类依赖添加的对象的 equals()

随机推荐