前言:

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


lambda表达式具体解释


一.问题

1.什么是lambda表达式?

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

3.lambda表达式的优缺点?

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

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


二.概念

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

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


三.先看看效果

先看几个样例:

1.使用lambda表达式实现Runnable

package com.lambda;

/**
 * 使用lambda表达式替换Runnable匿名内部类
 * @author MingChenchen
 *
 */
public class RunableTest {
    /**
     * 普通的Runnable
     */
    public static void runSomeThing(){

        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                System.out.println("I am running");
            }
        };
        new Thread(runnable).start();
    }

    /**
     * 使用lambda后的
     */
    public static void runSomeThingByLambda(){
        new Thread(() -> System.out.println("I am running")).start();
    }

    public static void main(String[] args) {
        runSomeThing();
//      runSomeThingByLambda();
    }
}

上述代码中:
() -> System.out.println("I am running")就是一个lambda表达式,
能够看出,它是替代了new Runnable(){}这个匿名内部类。

2.使用lambda表达式实现Comparator

package com.lambda;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class SortList {
    //给入一个List
    private static List<String> list =
            Arrays.asList("my","name","is","uber","and","uc");

    /**
     * 对一个String的list进行排序 - 使用老方法
     */
    public static void oldSort(){
        //排序
        Collections.sort(list,new Comparator<String>() {
            //使用新的排序规则 依据第二个字符进行逆序排
            @Override
            public int compare(String a,String b){
                if (a.charAt(1) <= b.charAt(1)) {
                    return 1;
                }else{
                    return -1;
                }
            }
        });
    }

    /**
     * 新的排序方法 - 使用lambda表达式实现
     */
    public static void newSort(){
        //lambda会自己主动判断出 a,b 的类型
        Collections.sort(list, (a, b) -> a.charAt(1) < b.charAt(1) ? 1:-1);
    }

    public static void main(String[] args) {
//      oldSort();
        newSort();
    }
}

3.使用lambda表达式实现ActionListener

package com.lambda;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;

public class ActionEventDemo {
    private JButton button = new JButton();

    public void bindEvent(){
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("你好!

" );

            }
        });
    }

    /**
     * 使用Lambda表达式 为button加入ActionListener
     */
    public void bindEventByLambda(){
        button.addActionListener(e -> System.out.println("你好!"));
    }
}

四.来由

好了,通过上述的几个样例,大家几乎相同也能明确了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. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y;

//2. 接受一个 string 对象,并在控制台打印,不返回不论什么值(看起来像是返回void)
(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,它里面包括了经常使用的函数式接口,比如:

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

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


八.很多其它的样例

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

package com.lambda.myaction;

/**
 * 自己定义一个函数式接口
 * @author MingChenchen
 *
 */
public interface MyActionInterface {
    public void saySomeThing(String str);
    /**
     * Java8引入的新特性 接口中能够定义一个default方法的实现 (不是abstract)
     */
    default void say(){
        System.out.println("default say");

    }
}
package com.lambda.myaction;

/**
 * 在我们自己定义的函数式接口的地方使用lambda表达式
 * @author MingChenchen
 *
 */
public class WantSay {
    /**
     * 运行接口中的saySomeThing方法
     * @param action
     * @param thing
     */
    public static void excuteSay(MyActionInterface action,String thing){
        action.saySomeThing(thing);
    }

    public static void main(String[] args) {
        /*
        excuteSay(new MyActionInterface(){
            @Override
            public void saySomeThing(String str) {
                System.out.println(str);
            }
        },"Hello World");
        */

        excuteSay((String s) -> System.out.println(s),"Hello world new");

    }
}

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

package com.lambda.usebean;

/**
 * 实体类Person
 * @author MingChenchen
 *
 */
public class Person {
    private String name;      //姓名
    private String location;  //地址

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getLocation() {
        return location;
    }
    public void setLocation(String location) {
        this.location = location;
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "Person:" + name + "," + location;
    }
}
//使用String默认的排序规则,比較的是Person的name字段
Comparator<Person> byName = Comparator.comparing(p -> p.getName());
//不用写传入參数,传入的用Person来声明
Comparator<Person> byName2 = Comparator.comparing(Person::getName);

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

//原本的for-each循环写做法
List list = Arrays.asList(....);
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

//使用lambda表达式后的写法
list.forEach(str -> System.out.println(str));

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

interface List<E> extends Collection<E>
interface Collection<E> extends Iterable<E>

public interface Iterable<T> {
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
}

4.一个完整的样例

//普通写法:
List<Person> people = ...
Collections.sort(people, new Comparator<Person>() {
  public int compare(Person x, Person y) {
    return x.getLastName().compareTo(y.getLastName());
  }
})

//使用lambda表达式写法:
people.sort(comparing(Person::getLastName));

化简流程:

第一步:去掉冗余的匿名类
Collections.sort(people,(Person x, Person y) -> x.getLastName().compareTo(y.getLastName()));

第二步:使用Comparator里的comparing方法
Collections.sort(people, Comparator.comparing((Person p) -> p.getLastName()));

第三步:类型推导和静态导入
Collections.sort(people, comparing(p -> p.getLastName()));

第四步:方法引用
Collections.sort(people, comparing(Person::getLastName));

第五步:使用List本身的sort更优
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?

官方解释:

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(过滤)

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

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

//把person转成Adult
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岁的总人数

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

2.Collect(收集流的结果)

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

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

五.顺序流和并行流

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

//顺序流:
List <Person> people = list.getStream.collect(Collectors.toList());

//并行流:
List <Person> people = list.getStream.parallel().collect(Collectors.toList());

//能够看出,要使用并行流,仅仅须要.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.

package com.lambda.stream;

import java.util.stream.IntStream;

public class TestPerformance {
    public static void main(String[] args) {
        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);

    }
    }

运行结果:

serial: 0.07s
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/

$(function () {
$('pre.prettyprint code').each(function () {
var lines = $(this).text().split('\n').length;
var $numbering = $('

    ').addClass('pre-numbering').hide();
    $(this).addClass('has-numbering').parent().append($numbering);
    for (i = 1; i ').text(i));
    };
    $numbering.fadeIn(1700);
    });
    });

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

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

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

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

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

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

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

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

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

  5. 【Java学习笔记之三十一】详解Java8 lambda表达式

    Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前 ...

  6. Java8 Lambda表达式和流操作如何让你的代码变慢5倍

    原文出处:ImportNew 有许许多多关于 Java 8 中流效率的讨论,但根据 Alex Zhitnitsky 的测试结果显示:坚持使用传统的 Java 编程风格——iterator 和 for- ...

  7. java8之lambda表达式(1)-基本语法

    lambda表达式,即带有参数的表达式,为更清晰地理解lambda表达式,先看如下例子: (1) class Student{ private String name; private Double ...

  8. Android Stutio中使用java8的Lambda表达式

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51532028 本文出自:[openXu的博客] 目录: 为什么要使用Lambda表达式 让A ...

  9. Java 8 vs. Scala(一): Lambda表达式

    [编者按]虽然 Java 深得大量开发者喜爱,但是对比其他现代编程语言,其语法确实略显冗长.但是通过 Java8,直接利用 lambda 表达式就能编写出既可读又简洁的代码.作者 Hussachai ...

随机推荐

  1. C++常见笔试面试要点以及常见问题

    1. C++常见笔试面试要点: C++语言相关: (1) 虚函数(多态)的内部实现 (2) 智能指针用过哪些?shared_ptr和unique_ptr用的时候需要注意什么?shared_ptr的实现 ...

  2. Android自定义控件之自定义ViewGroup实现标签云

    前言: 前面几篇讲了自定义控件绘制原理Android自定义控件之基本原理(一),自定义属性Android自定义控件之自定义属性(二),自定义组合控件Android自定义控件之自定义组合控件(三),常言 ...

  3. 【转】封装原生JS实现Ajax

    function createXHR() { if (window.XMLHttpRequest) { //IE7+.Firefox.Opera.Chrome 和Safari return new X ...

  4. Redis-Sentinel(Redis集群监控管理)

    Redis的高可用方案的实现:主从切换以及虚拟IP或客户端 从Redis 2.8开始加入对Sentinel机制从而实现了服务器端的主从切换,但目前尚未发现实现虚拟IP或客户端切换方案 Redis-Se ...

  5. JS写随机数

    使用JS编写一个方法 让数组中的元素每次刷新随机排列(不得使用sort方法:需注明步骤思路).例如:初始数组:a,b,c,d 每次刷新页面都会显示不同:b,c,d,a….a,d,c,b…等等 代码: ...

  6. Flume采集处理日志文件

    Flume简介 Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据:同时,Flume提供对数据 ...

  7. MongoDB 分片的原理、搭建、应用

    一.概念: 分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程.将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载.基本思想就是将集合切成小块,这 ...

  8. Oracle常用命令

    查看数据库锁的情况 SELECT object_name, machine, s.sid, s.serial#  FROM gv$locked_object l, dba_objects o, gv$ ...

  9. Windows中多个python版本共存的问题

    原创文章,未经本人允许进制转载. 在我的Windows中,先安装了python3.4,后来因为需要又安装了python2.7,结果发现: 直接双击1.py和命令行python 1.py使用的pytho ...

  10. 每天一个linux命令(16):whereis 命令

    whereis命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b).man说明文件(参数-m)和源代码文件(参数-s).如果省略参数,则返回所有信息. 和 find相比,whereis查找的速度 ...