前言:

本人也是学习lambda不久,可能有些地方描写叙述有误,还请大家谅解及指正!


lambda表达式具体解释


一.问题

1.什么是lambda表达式?

2.lambda表达式用来干什么的?

3.lambda表达式的优缺点?

4.lambda表达式的使用场景?

5.lambda仅仅是一个语法糖吗?


二.概念

lambda表达式是JAVA8中提供的一种新的特性。它支持JAVA也能进行简单的“函数式编程”。

它是一个匿名函数,Lambda表达式基于数学中的λ演算得名。直接相应于当中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。


三.先看看效果

先看几个样例:

1.使用lambda表达式实现Runnable

  1. package com.lambda;
  2. /**
  3. * 使用lambda表达式替换Runnable匿名内部类
  4. * @author MingChenchen
  5. *
  6. */
  7. public class RunableTest {
  8. /**
  9. * 普通的Runnable
  10. */
  11. public static void runSomeThing(){
  12. Runnable runnable = new Runnable() {
  13. @Override
  14. public void run() {
  15. System.out.println("I am running");
  16. }
  17. };
  18. new Thread(runnable).start();
  19. }
  20. /**
  21. * 使用lambda后的
  22. */
  23. public static void runSomeThingByLambda(){
  24. new Thread(() -> System.out.println("I am running")).start();
  25. }
  26. public static void main(String[] args) {
  27. runSomeThing();
  28. // runSomeThingByLambda();
  29. }
  30. }
  31. 上述代码中:
  32. () -> System.out.println("I am running")就是一个lambda表达式,
  33. 能够看出,它是替代了new Runnable(){}这个匿名内部类。

2.使用lambda表达式实现Comparator

  1. package com.lambda;
  2. import java.util.Arrays;
  3. import java.util.Collections;
  4. import java.util.Comparator;
  5. import java.util.List;
  6. public class SortList {
  7. //给入一个List
  8. private static List<String> list =
  9. Arrays.asList("my","name","is","uber","and","uc");
  10. /**
  11. * 对一个String的list进行排序 - 使用老方法
  12. */
  13. public static void oldSort(){
  14. //排序
  15. Collections.sort(list,new Comparator<String>() {
  16. //使用新的排序规则 依据第二个字符进行逆序排
  17. @Override
  18. public int compare(String a,String b){
  19. if (a.charAt(1) <= b.charAt(1)) {
  20. return 1;
  21. }else{
  22. return -1;
  23. }
  24. }
  25. });
  26. }
  27. /**
  28. * 新的排序方法 - 使用lambda表达式实现
  29. */
  30. public static void newSort(){
  31. //lambda会自己主动判断出 a,b 的类型
  32. Collections.sort(list, (a, b) -> a.charAt(1) < b.charAt(1) ? 1:-1);
  33. }
  34. public static void main(String[] args) {
  35. // oldSort();
  36. newSort();
  37. }
  38. }

3.使用lambda表达式实现ActionListener

  1. package com.lambda;
  2. import java.awt.event.ActionEvent;
  3. import java.awt.event.ActionListener;
  4. import javax.swing.JButton;
  5. public class ActionEventDemo {
  6. private JButton button = new JButton();
  7. public void bindEvent(){
  8. button.addActionListener(new ActionListener() {
  9. @Override
  10. public void actionPerformed(ActionEvent e) {
  11. System.out.println("你好!
  12. " );
  13. }
  14. });
  15. }
  16. /**
  17. * 使用Lambda表达式 为button加入ActionListener
  18. */
  19. public void bindEventByLambda(){
  20. button.addActionListener(e -> System.out.println("你好!"));
  21. }
  22. }

四.来由

好了,通过上述的几个样例,大家几乎相同也能明确了lambda是用来干什么以及优点了。

显而易见的。优点就是代码量大大降低了!

程序逻辑也非常清晰明了。

它的用处浅显来说就是替代“内部匿名类”、能够对集合或者数组进行循环操作。

曾经:

面向对象式编程就应该纯粹的面向对象,于是经常看到这样的写法:

假设你想写一个方法,那么就必须把它放到一个类里面。然后new出来对象,对象调用这种方法。

匿名类型最大的问题就在于其冗余的语法。

有人戏称匿名类型导致了“高度问题”(height problem):

比方大多匿名内部类的多行代码中仅有一行在做实际工作。

因此JAVA8中就提供了这样的“函数式编程”的方法 —— lambda表达式,供我们来更加简明扼要的实现内部匿名类的功能。


五.什么时候能够使用它?

先说一个名词的概念

函数式接口:Functional Interface.

定义的一个接口,接口里面必须 有且仅仅有一个抽象方法 ,这样的接口就成为函数式接口。

在能够使用lambda表达式的地方。方法声明时必须包括一个函数式的接口。

JAVA8的接口能够有多个default方法

不论什么函数式接口都能够使用lambda表达式替换。

比如:ActionListener、Comparator、Runnable

lambda表达式仅仅能出如今目标类型为函数式接口的上下文中。

注意:

此处是仅仅能

!!

意味着假设我们提供的这个接口包括一个以上的Abstract Method,那么使用lambda表达式则会报错。

这点已经验证过了。

场景:

这样的场景事实上非经常见:

你在某处就真的仅仅须要一个能做一件事情的函数而已,连它叫什么名字都无关紧要。

Lambda 表达式就能够用来做这件事。


六.写法、规则

基本的语法:

(parameters) -> expression 或 (parameters) ->{ statements; }

即: 參数 -> 带返回值的表达式/无返回值的陈述

  1. //1. 接收2个int型整数,返回他们的和
  2. (int x, int y) -> x + y;
  3. //2. 接受一个 string 对象,并在控制台打印,不返回不论什么值(看起来像是返回void)
  4. (String s) -> System.out.print(s);

七.几个特性

1. 类型推导

编译器负责推导lambda表达式的类型。它利用lambda表达式所在上下文所期待的类型进行推导,

这个被期待的类型被称为目标类型。就是说我们传入的參数能够无需写类型了!

2.变量捕获

在Java SE 7中。编译器对内部类中引用的外部变量(即捕获的变量)要求非常严格:

假设捕获的变量没有被声明为final就会产生一个编译错误。

我们如今放宽了这个限制——对于lambda表达式和内部类,

我们同意在当中捕获那些符合有效仅仅读(Effectively final)的局部变量。

简单的说,假设一个局部变量在初始化后从未被改动过,那么它就符合有效仅仅读的要求,

换句话说。加上final后也不会导致编译错误的局部变量就是有效仅仅读变量。

注意:此处和final关键字一样,指的是引用不可改!(感觉没多大意义,还不是用的final)

3.方法引用

假设我们想要调用的方法拥有一个名字,我们就能够通过它的名字直接调用它。

Comparator byName = Comparator.comparing(Person::getName);

此处无需再传入參数。lambda会自己主动装配成Person类型进来然后运行getName()方法。而后返回getName()的String

方法引用有非常多种,它们的语法例如以下:

静态方法引用:ClassName::methodName

实例上的实例方法引用:instanceReference::methodName

超类上的实例方法引用:super::methodName

类型上的实例方法引用:ClassName::methodName

构造方法引用:Class::new

数组构造方法引用:TypeName[]::new

4.JAVA提供给我们的SAM接口

Java SE 8中添加了一个新的包:java.util.function,它里面包括了经常使用的函数式接口,比如:

  1. Predicate<T>——接收T对象并返回boolean
  2. Consumer<T>——接收T对象。不返回值
  3. Function<T, R>——接收T对象。返回R对象
  4. Supplier<T>——提供T对象(比如工厂),不接收值
  5. UnaryOperator<T>——接收T对象,返回T对象
  6. BinaryOperator<T>——接收两个T对象,返回T对象

那么在參数为这些接口的地方,我们就能够直接使用lambda表达式了!


八.很多其它的样例

1.自己定义SAM接口。从而使用lambda表达式

  1. package com.lambda.myaction;
  2. /**
  3. * 自己定义一个函数式接口
  4. * @author MingChenchen
  5. *
  6. */
  7. public interface MyActionInterface {
  8. public void saySomeThing(String str);
  9. /**
  10. * Java8引入的新特性 接口中能够定义一个default方法的实现 (不是abstract)
  11. */
  12. default void say(){
  13. System.out.println("default say");
  14. }
  15. }
  1. package com.lambda.myaction;
  2. /**
  3. * 在我们自己定义的函数式接口的地方使用lambda表达式
  4. * @author MingChenchen
  5. *
  6. */
  7. public class WantSay {
  8. /**
  9. * 运行接口中的saySomeThing方法
  10. * @param action
  11. * @param thing
  12. */
  13. public static void excuteSay(MyActionInterface action,String thing){
  14. action.saySomeThing(thing);
  15. }
  16. public static void main(String[] args) {
  17. /*
  18. excuteSay(new MyActionInterface(){
  19. @Override
  20. public void saySomeThing(String str) {
  21. System.out.println(str);
  22. }
  23. },"Hello World");
  24. */
  25. excuteSay((String s) -> System.out.println(s),"Hello world new");
  26. }
  27. }

2.用法引用( ClassName::Method,无括号)

  1. package com.lambda.usebean;
  2. /**
  3. * 实体类Person
  4. * @author MingChenchen
  5. *
  6. */
  7. public class Person {
  8. private String name; //姓名
  9. private String location; //地址
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public String getLocation() {
  17. return location;
  18. }
  19. public void setLocation(String location) {
  20. this.location = location;
  21. }
  22. @Override
  23. public String toString() {
  24. // TODO Auto-generated method stub
  25. return "Person:" + name + "," + location;
  26. }
  27. }
  1. //使用String默认的排序规则,比較的是Person的name字段
  2. Comparator<Person> byName = Comparator.comparing(p -> p.getName());
  3. //不用写传入參数,传入的用Person来声明
  4. Comparator<Person> byName2 = Comparator.comparing(Person::getName);

3.使用lambda表达式完毕for-each循环操作

  1. //原本的for-each循环写做法
  2. List list = Arrays.asList(....);
  3. for (int i = 0; i < list.size(); i++) {
  4. System.out.println(list.get(i));
  5. }
  6. //使用lambda表达式后的写法
  7. list.forEach(str -> System.out.println(str));

list.forEach()是JAVA8的新方法。支持函数式编程,此处使用的參数就是JAVA提供给我们的函数式接口:Consumer< T>

  1. interface List<E> extends Collection<E>
  2. interface Collection<E> extends Iterable<E>
  3. public interface Iterable<T> {
  4. default void forEach(Consumer<? super T> action) {
  5. Objects.requireNonNull(action);
  6. for (T t : this) {
  7. action.accept(t);
  8. }
  9. }
  10. }

4.一个完整的样例

  1. //普通写法:
  2. List<Person> people = ...
  3. Collections.sort(people, new Comparator<Person>() {
  4. public int compare(Person x, Person y) {
  5. return x.getLastName().compareTo(y.getLastName());
  6. }
  7. })
  8. //使用lambda表达式写法:
  9. people.sort(comparing(Person::getLastName));

化简流程:

  1. 第一步:去掉冗余的匿名类
  2. Collections.sort(people,(Person x, Person y) -> x.getLastName().compareTo(y.getLastName()));
  3. 第二步:使用Comparator里的comparing方法
  4. Collections.sort(people, Comparator.comparing((Person p) -> p.getLastName()));
  5. 第三步:类型推导和静态导入
  6. Collections.sort(people, comparing(p -> p.getLastName()));
  7. 第四步:方法引用
  8. Collections.sort(people, comparing(Person::getLastName));
  9. 第五步:使用List本身的sort更优
  10. people.sort(comparing(Person::getLastName));;

九.优缺点

优点:

1.极大的简化代码。去除了非常多没用的Java代码,使得代码更为简洁明了。

2.比匿名内部类更加高效(不确定)。

编译器会生成专门的lambda方法。能够使用javap -p查看编译过后的代码

缺点:

1.可读性差。在代码简洁的情况下,还有一方面又让大多程序猿非常难读懂。由于非常少程序猿接触使用它。

(只是这个缺点不是本身缺点,并且源于程序猿较少使用)


十.它是一个语法糖吗?

答:

就我自身的理解来说,lambda表达式不算是一个语法糖。

语法糖就是说仅仅是帮助我们程序猿轻松的少写一些代码。之后编译器帮我们把那部分代码生成出来。

可是从编译过后的结果来说,并非自己主动帮我们生成一个内部匿名类,而是生成了一个lambda$X方法。

第二个就是lambda事实上表达的是眼下流行的“函数式编程”这样的思维。差别于我们面向对象的思维方法。

这点我觉得非常有意义,即我们要从各种思维来对待事情。而不是说,面向对象的这样的方法就是最NB的。

可是论坛基本都觉得这是一个语法糖,也没错。毕竟它提倡的仅仅是一种思想,并且jdk底层为lambda生成了新的高效的代码这个事儿并不确定。


接下来介绍 lambda的 好哥们:stream.

stream的方法里面大多都使用了lambda表达式

stream概要


一.什么是stream?

官方解释:

  1. A sequence of elements supporting sequential and parallel aggregate operations.

简单来讲,stream就是JAVA8提供给我们的对于元素集合统一、高速、并行操作的一种方式。

它能充分运用多核的优势。以及配合lambda表达式、链式结构对集合等进行很多实用的操作。

概念:

stream:能够支持顺序和并行对元素操作的元素集合。

作用:

提供了一种操作大数据接口。让数据操作更easy和更快

使用stream,我们能够对collection的元素进行过滤、映射、排序、去重等很多操作。

中间方法和终点方法:

它具有过滤、映射以及降低遍历数等方法,这些方法分两种:中间方法和终端方法,

“流”抽象天生就该是持续的,中间方法永远返回的是Stream,因此假设我们要获取终于结果的话。

必须使用终点操作才干收集流产生的终于结果。区分这两个方法是看他的返回值,

假设是Stream则是中间方法,否则是终点方法


二.怎样使用stream?

1.通过Stream接口的静态工厂方法(注意:Java8里接口能够带静态方法);

2.通过Collection接口的默认方法(默认方法:Default method,也是Java8中的一个新特性,就是接口中的一个带有实现的方法)–stream(),把一个Collection对象转换成Stream

普通情况下,我们都使用Collection接口的 .stream()方法得到stream.


三.常见的几个中间方法

中间方法即是一些列对元素进行的操作。譬如过滤、去重、截断等。

1.Filter(过滤)

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

2.Map(对元素进行操作)

  1. //把person转成Adult
  2. Stream map = persons.stream().filter(p -> p.getAge() > 18).map(person -> new Adult(person));

3.limit(截断)

对一个Stream进行截断操作,获取其前N个元素。假设原Stream中包括的元素个数小于N,那就获取其全部的元素

4.distinct(去重)

对于Stream中包括的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有反复的元素


四.经常使用的终点方法

通过中间方法,我们对stream的元素进行了统一的操作,可是中间方法得到还是一个stream。要想把它转换为新的集合、或者是统计等。

我们须要使用终点方法。

1.count(统计)

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

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

2.Collect(收集流的结果)

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

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

五.顺序流和并行流

每一个Stream都有两种模式:顺序运行和并行运行

  1. //顺序流:
  2. List <Person> people = list.getStream.collect(Collectors.toList());
  3. //并行流:
  4. List <Person> people = list.getStream.parallel().collect(Collectors.toList());
  5. //能够看出,要使用并行流,仅仅须要.parallel()就可以

顾名思义。当使用顺序方式去遍历时。每一个item读完后再读下一个item。

而使用并行去遍历时,数组会被分成多个段,当中每一个都在不同的线程中处理,然后将结果一起输出。

并行流原理:

List originalList = someData;

split1 = originalList(0, mid);//将数据分小部分

split2 = originalList(mid,end);

new Runnable(split1.process());//小部分运行操作

new Runnable(split2.process());

List revisedList = split1 + split2;//将结果合并

性能:假设是多核机器。理论上并行流则会比顺序流快上一倍。

下面是借用他人的一个測试两者性能的Demo.

  1. package com.lambda.stream;
  2. import java.util.stream.IntStream;
  3. public class TestPerformance {
  4. public static void main(String[] args) {
  5. long t0 = System.nanoTime();
  6. //初始化一个范围100万整数流,求能被2整除的数字,toArray()是终点方法
  7. int a[]=IntStream.range(0, 1_000_000).filter(p -> p % 2==0).toArray();
  8. long t1 = System.nanoTime();
  9. //和上面功能一样,这里是用并行流来计算
  10. int b[]=IntStream.range(0, 1_000_000).parallel().filter(p -> p % 2==0).toArray();
  11. long t2 = System.nanoTime();
  12. //我本机的结果是serial: 0.06s, parallel 0.02s,证明并行流确实比顺序流快
  13. System.out.printf("serial: %.2fs, parallel %.2fs%n", (t1 - t0) * 1e-9, (t2 - t1) * 1e-9);
  14. }
  15. }

运行结果:

  1. serial: 0.07s
  2. parallel 0.02s

能够看出,并行流的效率确实提高了3.5倍(我本机是4核,电脑较差.)


进阶学习:

1.Predicate和Consumer接口– Java 8中java.util.function包下的接口:

http://ifeve.com/predicate-and-consumer-interface-in-java-util-function-package-in-java-8/

2.深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)

http://zh.lucida.me/blog/java-8-lambdas-insideout-language-features/


參考资料:

1.Java8初体验(二)Stream语法具体解释:

http://ifeve.com/stream/

2.Java8初体验(一)lambda表达式语法

http://ifeve.com/lambda/

JAVA8之lambda表达式具体解释,及stream中的lambda使用的更多相关文章

  1. 初探Lambda表达式/Java多核编程【4】Lambda变量捕获

    这周开学,上了两天感觉课好多,学校现在还停水,宿舍网络也还没通,简直爆炸,感觉能静下心看书的时间越来越少了...寒假还有些看过书之后的存货,现在写一点发出来.加上假期两个月左右都过去了书才看了1/7都 ...

  2. Lambda01 编程范式、lambda表达式与匿名内部类、函数式接口、lambda表达式的写法

    1 编程范式 主要的编程范式有三种:命令式编程,声明式编程和函数式编程. 1.1 命令式编程 关注计算机执行的步骤,就是告诉计算机先做什么后做什么 1.2 声明式编程 表达程序的执行逻辑,就是告诉计算 ...

  3. 初探Lambda表达式/Java多核编程【3】Lambda语法与作用域

    接上一篇:初探Lambda表达式/Java多核编程[2]并行与组合行为 本节是第二章开篇,前一章已经浅显地将所有新概念点到,书中剩下的部分将对这些概念做一个基础知识的补充与深入探讨实践. 本章将介绍L ...

  4. java语言中的匿名类与lambda表达式介绍与总结 (Anonymous Classes and Lambda Expressions)

    2017/6/30 转载写明出处:http://www.cnblogs.com/daren-lin/p/anonymous-classes-and-lambda-expressions-in-java ...

  5. 有些lambda表达式就可以体现出编程中「Context(上下文)」环境

    编程中什么是「Context(上下文)」?   每一段程序都有很多外部变量.只有像Add这种简单的函数才是没有外部变量的.一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行.你为了使他们运行 ...

  6. lambda表达式在python和c++中的异同

    Lambda表达式是干么的?.lambda表达式首先是一个表达式,是一个函数对象一个匿名函数,但不是函数.现在流行语言例如:JS.PHP都支持一种和面向过程.面向对象并列的函数式编程,lambda就是 ...

  7. JAVA8之lambda表达式详解,及stream中的lambda使用

    分享文章:https://blog.csdn.net/jinzhencs/article/details/50748202

  8. Java 8新特性:新语法方法引用和Lambda表达式及全新的Stream API

    新语法 方法引用Method references Lambda语法 Lambda语法在AndroidStudio中报错 Stream API 我正参加2016CSDN博客之星的比赛 希望您能投下宝贵 ...

  9. jdk1.8新特性之lambda表达式及在Android Studio中的使用举例

    Jdk1.8已经出很久了但是很多同学对它的特性在android studio 中的应用可能还不是很熟悉,今天我们就来对这个新特性在AS中做它的应用实践. 一.首先在有JDK1.8的情况下我们要在AS的 ...

随机推荐

  1. gulp learning note

    为啥写这一片文章呢? 主要是为了温故而知新和分享,也是为了更加促进自己的学习! 前端自动化工具很多  有grunt  gulp  webpack 等 这次主要分享下gulp的学习经验,让自己更好的总结 ...

  2. RE : 球体波浪倒计时

    背景: 移动端需要做一个倒计时球体水波的效果.主要用到了CSS的SVG瞄点动画和JS的计时器.该动画原型来自于  使用球体水面波动显示进度动画 http://wow.techbrood.com/fid ...

  3. Scrum Meeting Alpha - 5

    Scrum Meeting Alpha - 5 NewTeam 2017/10/20 地点:主楼与4号楼之间的走廊2楼 任务反馈 团队成员 完成任务 计划任务 安万贺 完成了对班级作业部分的API的包 ...

  4. python+selenium安装

    1.下载Python 请到官网自行下载安装https://www.python.org/downloads/ 在安装的时候,注意一定要勾上这个选项,可以免去我们配置系统变量的麻烦,如果你忘了,没关系, ...

  5. Javascript parseFloat、parseDouble类型转换,数值加减,四舍五入

    <script language="JavaScript">var a = "0.11";var b = "0.2801";va ...

  6. 人生苦短,python是岸.

    人生苦短,python是岸. 愿付一生,应许之诚.

  7. [转载] 详细讲解Hadoop中的简单数据库HBase

    转载自http://www.csdn.net/article/2010-11-28/282614 数据模型 HBase数据库使用了和Bigtable非常相似的数据模型.用户在表格里存储许多数据行.每个 ...

  8. [转]ORACLE分区表的使用和管理

    转自:http://love-flying-snow.iteye.com/blog/573303 废话少说,直接讲分区语法. Oracle表分区分为四种:范围分区,散列分区,列表分区和复合分区. 一: ...

  9. 使用JavaMail发送带附件的邮件

    所需jar包 链接:http://pan.baidu.com/s/1dFo4cDz 密码:akap 工具类: package com.javamail.utils; import java.util. ...

  10. swizzle method 和消息转发机制的实际使用

    我的工程结构,如图 1-0 图  1-0 在看具体实现以前,先捋以下 实现思路. ViewController 中有一个-(void)Amethod;A方法. -(void)Amethod{ NSLo ...