Java8中Lambda表达式的理解和运用

目录:java

1、简介程序员

2、函数接口编程

3、Lambda表达式api

4、目标类型数组

5、流闭包

6、function包并发

7、对并发的影响app

正文函数式编程

1、简介函数

一、java中lambda的由来

开发类库的程序员使用java时,发现抽象级别还不够,尤为是面对大型数据集合时,java还欠缺高效的并行操做。为了编写这类处理批量数据的并行类库,就须要在为java增长lambda表达式。

再如,当咱们定义一个线程类时,能够将该类实现Runnable接口(该接口只有一个方法run()),并实现run方法便可。但大多数状况下咱们可能并不这么作,由于该线程可能只会被使用一次,此时咱们通常会使用匿名内部类将线程的行为进行内联,代码举例以下:

可是匿名内部类存在缺陷:

  1. 语法过于冗余
  2. 匿名内部类中的this和变量名容易令人产生误解
  3. 没法捕获非final的局部变量

等等,因为上述缺陷,须要借助函数式编程得以解决。

2、函数式接口

咱们对面向对象编程并不陌生,面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象。现实世界中,数据和行为是并存的。

那么什么是函数式编程呢?其核心思想是:在思考问题时,使用不可变值和函数,函数对一个值进行处理,映射成另外一个值。

函数式接口定义:把只有一个抽象方法的接口称为函数式接口,可用作lambda表达式的类型。java中的函数式接口如Runnable、Comparator、Callable等。

如何自定义一个函数式接口?其实咱们并须要额外的工做来声明一个接口是函数式接口,编译器会根据接口的结构自行判断,固然并不是简单对接口中的方法进行计数。另外,java api提供了@FunctionalInterface注解来显示声明一个接口为函数式接口,加上该注解后,编译器就会验证该接口是否知足函数式接口的要求。

java8中增长了一个新的package:java.util.function,里面包含了经常使用的函数式接口。

3、Lambda表达式

lambda表达式又被称为闭包或匿名方法。

下面对lambda进行一些简单举例:

  1. (int x, int y) -> x + y    表示:接收整形参数x和y,并返回他们的和
  2. () -> 66                        表示:没有参数传入,直接返回数字66
  3. (String s) -> { System.out.println(s); }    表示:接收一个字符串型的参数,并将它输出到控制台,不返回值

因而可知,lambda表达式由参数列表、箭头符号(->)和函数体组成,其中函数体既能够是一个表达式,也能够是一个语句块。表达式函数体适用于小型lambda表达式,它省略了return关键字,使语法更简洁。

在使用lambda表达式时,能够显示声明参数的类型,如:(int x, int y) -> x + y,其实也能够省略参数类型,让编译器本身去推导出来,如:(x, y) -> x + y。

4、目标类型

一、目标类型

编译器负责推导lambda表达式的类型,它利用lambda表达式所在上下文所期待的类型进行推导,这个被期待的类型就是目标类型。lambda只能出如今目标类型为函数接口的上下文中。

lambda表达式对目标类型也是有要求的,当下面全部条件都成立时,lambda表达式才会赋给目标类型T。

  • T是一个函数式接口
  • lambda表达式的参数和T的方法参数在数量和类型上一一对应
  • lambda表达式的返回值和T的方法返回值相兼容
  • lambda表达式内所抛出的异常和T方法的throws类型相兼容

二、目标类型的上下文包括:

  • 变量声明:Callable<Integer> callable;
  • 赋值       :  callable = () -> "hello";
  • 返回语句 : public Runnable runner() { return () -> { System.out.println("hello"); }; }
  • 数组初始化器 : FileFilter[] fileFilters = new FileFilter[] { f -> f.exists(), f -> f.canRead(), f -> f.getName().startsWith("q") };
  • 方法或构造方法的参数 :
    方法或构造方法参数对目标类型的确认会涉及到其它两个语言特性:重载解析和参数类型推导。若是lambda具备显示类型(参数类型被显示指定),编译器就能够直接使用lambda表达式的返回类型;若是lambda表达式具备隐式类型(参数类型被推导而知),重载解析则会忽略lambda表达式函数体而只依赖lambda表达式参数的数量。若是在解析方法声明时存在二义性,咱们就须要利用转型或显示提供lambda表达式类型。举例:
  • lambda表达式函数体  : Supplier<Runnable> supplier = () -> () -> { System.out.println("hi"); };
  • 条件表达式(? :): Callable<Integer> c = true ? (() -> 23) : (() -> 42);
  • 转型表达式(cast): Runnable o = (Runnable) () -> { System.out.println("hi"); };

三、词法做用域

lambda引用的是值,而不是变量。在匿名内部类中引用它所在方法的变量时,该变量必须声明为final,虽然Java8中放宽了此限制,能够引用非final变量,可是该变量在既成事实上必须是final的,即不能再匿名内部类中改变它所在方法的变量值,不然编译器会报错。既成事实上的final是指只能给该变量赋值一次,换句话说,Lambda引用的是值,而不是变量。举例以下:

       

上面两个例子中,只要在内部类或lambda表达式中修改其所在方法中的变量都会报错。lambda表达式不支持修改外部变量的另外一个缘由是:咱们可使用更好的方式实现相同的效果:使用规约。java.util.function包中提供了各类规约,如sum、min、max等。

在内部类中定义和外部类中相同的变量时,其内部类中定义的变量会覆盖外部类中定义的变量。而lambda表达式基于词法做用域,在lambda函数体里面不容许定义和外面相同的变量,若是定义则会报错。举例:

所以,能够说:lambda表达式对值封闭,对变量开放。

5、流

一、概念

外部迭代:经过Iterator的方式进行迭代的过程称为外部迭代。

内部迭代:经过stream的方式进行迭代的过程称为内部迭代。

流 - Stream:是用函数式编程的方式在集合类上进行复杂操做的工具。java8中为集合类和数组都提供了转为Stream的方法,由集合或数组生成的Stream不是一个新集合,而是建立新集合的配方。

惰性求值方法:只描述或刻画Stream,最终不产生新集合的方法叫惰性求值方法,如Stream的filter方法。

及早求值方法:最终会生成新集合的方法叫及早求值方法,如Stream的count方法。

说明:判断一个方法是惰性求值仍是及早求值很简单:只需看它的返回值,若是返回值是Stream,那么就是惰性求值方法,若是返回的是另外一个值或空,就是及早求值方法。使用这些操做的理想方式就是造成一个惰性求值的链,最后用一个及早求值的操做返回想要的结果。

二、经常使用的流操做

2.1 collect(Collector<? super T,A,R>)

做用:该方法由Stream里的值生成一个列表,是一个及早求值方法。

举例:

List<String> list = Stream.of("a", "1", "b", "2").collect(Collectors.toList());

2.2 Stream<R> map(Function<? super T, ? super R>)

做用:将一个流中的值转换成一个新流,是一个惰性求值方法。

举例:

Stream<String> stream = Stream.of("a","b","c").map(one -> one.toUpperCase());

2.3 Stream<T> filter(Predicate<? super T>)

做用:遍历流中的数据并根据条件进行过滤,造成一个新流,是一个惰性求值方法。

举例:

Stream<String> stream1 = Stream.of("abc","bcd","def").filter(one -> one.contains("bc"));

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

做用:当流中包含多个列表时,可将这多个列表中的内容打平放到一个列表下的流中,是一个惰性求值方法。

举例:

Stream<String> stream2 = Stream.of(Arrays.asList("a","b","c"), Arrays.asList("abc","bcd","def")).flatMap(one -> one.stream());

2.5 Optional<T> max(Comparator<? super T>)和Optional<T> min(Comparator<? super T>)

做用:获取一个集合中的最大值和最小值,是两个及早求值方法

举例:

Integer max = Stream.of(1,2,3).max(Comparator.comparing(one->one)).get();
Integer min = Stream.of(1,2,3).min(Comparator.comparing(one->one)).get();

2.6 long count()

做用:返回Stream中元素的数量,是一个及早求值方法

举例:

Long count = Stream.of(1,2,3).count();

2.7 Optional<T> reduce(BinaryOperator<T>)

做用:从stream的一组值中生成1个值,count、max、min都属于reduce操做,是一个及早求值方法。

举例:第1个参数one是上次函数计算的返回值,其初始值是stream中第1个值;第2个参数two是stream中的元素值,其初始值是stream中第2个值,返回类型是Optional,经过get方法获取其值

Integer count = Stream.of(1,2,3,4,5,6).reduce((one, two) -> { System.out.println(one + ":"+two);return two; }).get();

2.8 T reduce(T, BinaryOperator<T>)

举例:该方法比上一个方法多了一个初始值100,one仍然表明上次函数计算的返回值,其初始值为就是设定的初始值100;two仍然是stream中的元素值,其初始值是stream中的第1个值。因为该方法指定了初始值,所以该方法的返回值类型就是设定的初始值类型,即Integer。

Integer count1 = Stream.of(1,2,3,4,5,6).reduce(100, (one, two) -> { System.out.println(one + ":"+two);return two; });

2.9 Stream<T> distinct()

做用:返回由该流的不一样元素组成的一个新流,是一个惰性求值方法

举例:

Stream.of(3,2,5,3,4,3).distinct().peek(one -> System.out.println(one)).count();

2.10 Stream<T> limit(int maxSize)

做用:返回前maxSize个元素组成的新流,是一个惰性求值方法

举例:

System.out.println(Stream.of(1,2,3,4,5,6).limit(3).filter(one -> {System.out.println(one);return true;}).count());

2.11 Stream<T> peek(Consumer<? super T>)

做用:返回一个由该流的元素组成的新流,而且对新流中每一个元素执行指定的操做

举例:

Stream stream = Stream.of(1,2,3,4,5,6).peek(one -> System.out.println(one));

2.12 Stream<T> skip(long n)

做用:从流的第1个元素开始,跳过n个元素,把从第n+1个元素开始到最后全部的元素造成一个新流

举例:

Stream.of(1,2,3,4,5,6).skip(3).peek(one -> System.out.println(one)).count();

2.13 Stream<T> sorted()

做用:将流中的元素按照天然顺序进行排序,是一个惰性求值方法

举例:

Stream.of(6,2,5,3,4,1).peek(one-> System.out.println(one)).sorted().peek(one-> System.out.println(one)).count();

2.14 boolean allMatch(Predicate<? super T>)

做用:返回流中的全部元素是否都与条件匹配

举例:

System.out.println(Stream.of(6,2,5,3,4,1).allMatch(one -> one>3));

2.15 boolean anyMatch(Predicate<? super T>)

做用:返回流中是否存在与条件匹配的元素

举例:

System.out.println(Stream.of(6,2,5,3,4,1).anyMatch(one -> one>3));

2.16 boolean noneMatch(Predicate<? super T>)

做用:返回流中全部元素是否都不与条件匹配

举例:

System.out.println(Stream.of(6,2,5,3,4,1).noneMatch(one -> one>3));

2.17 findAny()

做用:返回描述流的一些元素的Optional对象,若是流为空,则返回一个空Optional

举例:

System.out.println(Stream.of(3,2,5,6,4,1).findAny().get());

2.18 findFirst()

做用:返回描述流的第一个元素的Optional对象,若是流为空,则返回一个空Optional

举例:

System.out.println(Stream.of(3,2,5,6,4,1).findAny().get());

2.19 void forEach(Consumer<? super T>)

做用:对流中每一个元素循环进行操做

举例:

Stream.of(3,2,5,6,4,1).forEach(one -> System.out.println(one));

2.20 Object[] toArray()

做用:返回一个包含流中元素的数组

举例:

Integer[] values = (Integer[])Stream.of(3,2,5,6,4,1).toArray();

6、function包

一、Predicate<T>

抽象方法为boolean test(T),传入T类型的参数,返回一个boolean类型值。可用于流中数值判断。

举例:

Predicate<String> predicate = one -> one.length() > 0;

二、Consumer<T>

抽象方法为void accept(T),传入T类型的参数,不返回值。可用于实现消费者

举例:

Consumer<Integer> consumer = one -> System.out.println(one);

三、Supplier<T>

抽象方法为String[] value(),无参数,返回一个字符串数组。可用于实现生产者

举例:

Supplier<Integer> supplier = () -> new Integer(100);

四、Function<T, R>

抽象方法为R apply(T),传入参数T,返回结果R。

举例:

Function<String, Integer> function = one -> one.length();

五、UnaryOperator<T>

抽象方法T apply(T),接收T对象,返回T对象。

举例:

UnaryOperator<String> unaryOperator = one -> "hello:" + one;

六、BinaryOperator<T>

抽象方法为T apply(T, T),接收2个T对象,返回T对象。

举例:

BinaryOperator<Integer> binaryOperator = (one, two) -> one + two;

7、对并发的影响

待续

相关文章
相关标签/搜索