java8新特性Lambda表达式

lambda的语法

其实Lambda表达式的本质只是一个"语法糖",我们可以使用更少的代码来实现同样的功能,它允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

表达式形式
(parameters) -> expression
(parameters) -> {statments;}
() ->{}  // 什么都不做
() ->{ "hello"; } 等价于 () ->{ return "hello"; }
Lambda表达式几个简单例子
// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值 (看起来像是返回void)  
(String s) -> System.out.print(s)

需要说明的是,不是每个接口都可以缩写成 Lambda 表达式。只有那些函数式接口(Functional Interface)才能缩写成 Lambda 表示式。

@FunctionalInterface

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
如定义了一个函数式接口如下:

@FunctionalInterface
interface GreetingService 
{
    void sayMessage(String message);
}

那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):

GreetingService greetService1 = message -> System.out.println("Hello " + message);

常用的@FunctionalInterface在包java.util.function中 它包含了很多类,所如下
在这里插入图片描述

其接口描述有:

接口描述
BiConsumer<T,U>代表了一个接受两个输入参数的操作,并且不返回任何结果
BiFunction<T,U,R>代表了一个接受两个输入参数的方法,并且返回一个结果
BinaryOperator代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
BiPredicate<T,U>代表了一个两个参数的boolean值方法
BooleanSupplier代表了boolean值结果的提供方
Consumer代表了接受一个输入参数并且无返回的操作
Function<T,R>接受一个输入参数,返回一个结果
Predicate接受一个输入参数,返回一个布尔值结果。
Supplier无参数,返回一个结果

我们通过使用Predicate来了解接口方法
已知有一个Apple类,它有weight属性和color属性,我们来通过重量来筛选出合格的苹果

public class LambdaUsage {
public static void main(String[] args) {

// list中有3个不同颜色重量的苹果
List<Apple> list = Arrays.asList(new Apple("green",120), new Apple("red",150),new Apple("yellow",80));

// filterByWeight方法中筛选 质量大于100的苹果 条件
List<Apple> result = filterByWeight(list, w -> w > 100);
// 或者用 stram filter 条件过滤, filter里填写Predict类型
List<Apple> result2 = list.stream().filter(p -> p.getWeight() > 100).collect(Collectors.toList());

System.out.println("result 1:"+result);
System.out.println("result 2:"+result2);

}

// 条件筛选出苹果
private static List<Apple> filterByWeight(List<Apple> source, LongPredicate predicate){
List<Apple> result = new ArrayList<>();
for (Apple a : source) {
    if (predicate.test(a.getWeight()))
        result.add(a);
}
return result;
}

}

输出结果
在这里插入图片描述

Supllier 生产者

它与Function不同,它不接受参数,直接为我们生产一个指定的结果,有点像生产者模式:

Supplier<String> supplier = String::new;
 String s = supplier.get();
 System.out.println(s);

Supplier 接口有一个专属符号“::”,它表示直接返回一个值;

Consumer消费者

我们需要提供入参,用来被消费,没有返回值,如下面这段示例代码:

Consumer<Apple> ap = a -> System.out.println("the color of "+a.getColor()+" apple have"+ a.getWeight()+" kg");
        ap.accept(new Apple("red",100));
Optional,避免空指针问题

空指针异常是我们开发常见的问题,通常我们会写大量的非空条件判断来避免空指针问题。Java 8引入了一个新的Optional类,很好的用于避免空指针的出现,下图为Optinal类的所有方法:
在这里插入图片描述

  • 使用Optional创建类
// 创建一个空对象果篮
Optional<Basket> basket1  = Optional.empty();//Optional.of(new Basket("bamboo-basket",10,null));     
// 创建一个非null的果篮Optional
Optional<Basket> basket2  = Optional.of(new Basket("bamboo-basket",10, Arrays.asList(new Apple("red",120),new Apple("blue",100))));
// 判断果篮是否为空值果篮        System.out.println("basket:"+basket1.isPresent());
  • get()获取对象
// 获取basket
Basket basket = basket2.get();
  • 判断对象是否为空 isPresent
// 判断果篮是否为空,不为空就输出果篮
if (basket2.isPresent())
System.out.println(basket2.toString());
  • 如果不为空,则执行ifPresent
//  如果这个篮子不为空,执行lambda表达式,反之空的就不会执行
basket1.ifPresent((bask)->{
System.out.println("这个篮子是:"+bask.toString());
        });
  • 如果对象为空,就指定一个对象 orElse()
// 如果对象为空,则将它指定为一个塑料篮子
List<Apple> list = Arrays.asList(new Apple("red",200), new Apple("blue",220), new Apple("yellow",210));
// 原来不存在,所以给它一个新的值
Basket basket11 = basket1.orElse(new Basket("plastic-basket", 10, list));
System.out.println("new basket11:"+basket11.toString());
// 原来存在,直接赋原来值
Basket basket21 = basket2.orElse(new Basket("plastic-basket", 10, list));
System.out.println("new basket21:"+basket21.toString());
  • 如果为空,抛出异常 orElseThrow
// 如果果篮为空,抛出异常
 basket1.orElseThrow(IllegalStateException::new);
  • filter过滤
// 判断出有苹果的果篮
basket2.filter(basket -> {return basket.getList().size()>0;}).ifPresent((basket)->{
System.out.println("果篮里有水果");
  • 综上代码
public class OptionalTest {
public static void main(String[] args) {
// 创建一个空对象果篮
Optional<Basket> basket1  = Optional.empty();//Optional.of(new Basket("bamboo-basket",10,null));
// 创建一个非null的果篮Optional
Optional<Basket> basket2  = Optional.of(new Basket("bamboo-basket",10, Arrays.asList(new Apple("red",120),new Apple("blue",100))));
// 判断果篮是否为空值果篮
System.out.println("basket:"+basket1.isPresent());

// 判断果篮是否为空,不为空就输出果篮
if (basket2.isPresent())
   System.out.println(basket2.toString());

//  如果这个篮子不为空,执行lambda表达式,反之空的就不会执行
basket1.ifPresent((bask)->{
   System.out.println("这个篮子是:"+bask.toString());
});

// 如果对象为空,则将它指定为一个塑料篮子
List<Apple> list = Arrays.asList(new Apple("red",200), new Apple("blue",220), new Apple("yellow",210));
// 原来不存在,所以给它一个新的值
Basket basket11 = basket1.orElse(new Basket("plastic-basket", 10, list));
System.out.println("new basket11:"+basket11.toString());
// 原来存在,直接赋原来值
Basket basket21 = basket2.orElse(new Basket("plastic-basket", 10, list));
System.out.println("new basket21:"+basket21.toString());

// 如果果篮为空,抛出异常
basket1.orElseThrow(IllegalStateException::new);

// 判断出有苹果的果篮
basket2.filter(basket -> {return basket.getList().size()>0;}).ifPresent((basket)->{
   System.out.println("果篮里有水果");
});
}
}

水果篮子类

// 水果篮
class Basket{
private String name;
private int size;
private List<Apple> list;


public Basket(String name, int size, List<Apple> list) {
    this.name = name;
    this.size = size;
    this.list = list;
}
// getter and setter...
@Override
public String toString() {
    return "Basket{" +
            "name='" + name + '\'' +
            ", size=" + size +
            ", list=" + list +
            '}';
}
}
Stream流

当我们使用一个流的时候,通常包括三个基本步骤:
获取一个数据源(source)→ 数据转换→执行操作获取想要的结果;
每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换);
这就允许对其操作可以像链条一样排列,变成一个管道;

几种常用的stream
  • map构造平方数
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
List<Integer> squareNums = nums.stream().map(n -> n * n).collect(Collectors.toList());
System.out.println("平方数:"+squareNums);

这里的map是一种映射关系,每输入一个元素,都按照规则转换成另外一个元素

  • filter 筛选出偶数
List<Integer> test = list.stream().filter(integer -> {
    return integer % 2 == 0;
}).collect(Collectors.toList());
System.out.println(test);

list为1到30的整数,通过筛选留下来满足规则的元素

  • sorted 倒排序
List<Integer> test2 = list.stream().sorted((a, b) -> {
    return b-a;
}).collect(Collectors.toList());
System.out.println("test2:"+test2);
  • Match匹配,这里分三种情况
    1、allMatch:Stream 中全部元素符合传入的 predicate,返回 true
    2、anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
    3、noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
  • 全部都是偶数
boolean b = list.stream().allMatch(integer -> {
            return integer % 2 == 0;
        });
System.out.println("全部是偶数:"+b);
  • 存在偶数
b = list.stream().anyMatch(integer -> {
            return integer % 2 == 0;
        });
System.out.println("存在偶数:"+b);
  • 全部都不是偶数
 b = list.stream().noneMatch(integer -> {
            return integer % 2 == 0;
        });
 System.out.println("都不是偶数:"+b);
  • forEach 每个数字前加上小星星
System.out.println("每个数字前加上一个\"*\":");
        list.stream().forEach(integer -> System.out.print("*"+integer+" "));
System.out.println("");
  • reduce
    这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。
    从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的reduce。
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println("1:"+concat);
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
System.out.println("2:"+minValue);
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
System.out.println("3:"+sumValue);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println("4:"+sumValue);
// 过滤,字符串连接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
System.out.println("5:"+concat);

  • 完整代码
public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();

    for (int i = 0; i < 30; i++) {
        list.add(i);
    }
    /**
     *  最常见的几种构造stream
     */

    // map 平方数,这里的map是一种映射关系,每输入一个元素,都按照规则转换成另外一个元素
    List<Integer> nums = Arrays.asList(1, 2, 3, 4);
    List<Integer> squareNums = nums.stream().map(n -> n * n).
            collect(Collectors.toList());
    System.out.println("平方数:"+squareNums);

    // filter 筛选出偶数,通过筛选留下来满足规则都元素
    List<Integer> test = list.stream().filter(integer -> {
        return integer % 2 == 0;
    }).collect(Collectors.toList());
    System.out.println(test);

    // 倒排序
    List<Integer> test2 = list.stream().sorted((a, b) -> {
        return b-a;
    }).collect(Collectors.toList());
    System.out.println("test2:"+test2);

    // Match,这里有3种情况
    /**
     * allMatch:Stream 中全部元素符合传入的 predicate,返回 true
     * anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
     * noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
     */
    // 全都是偶数
    boolean b = list.stream().allMatch(integer -> {
        return integer % 2 == 0;
    });
    System.out.println("全部是偶数:"+b);
    // 存在偶数
    b = list.stream().anyMatch(integer -> {
        return integer % 2 == 0;
    });
    System.out.println("存在偶数:"+b);
    // 都不是偶数
    b = list.stream().noneMatch(integer -> {
        return integer % 2 == 0;
    });
    System.out.println("都不是偶数:"+b);

    //  forEach ,每个数字前加上一个"*"
    System.out.println("每个数字前加上一个\"*\":");
    list.stream().forEach(integer -> System.out.print("*"+integer+" "));
    System.out.println("");

    /**
     * 这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),
     * 然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。
     * 从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。
     */
    // reduce
    // 字符串连接,concat = "ABCD"
    String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
    System.out.println("1:"+concat);
    // 求最小值,minValue = -3.0
    double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
    System.out.println("2:"+minValue);
    // 求和,sumValue = 10, 有起始值
    int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
    System.out.println("3:"+sumValue);
    // 求和,sumValue = 10, 无起始值
    sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
    System.out.println("4:"+sumValue);
    // 过滤,字符串连接,concat = "ace"
    concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
    System.out.println("5:"+concat);
}
其他
两个冒号代表什么

双冒号运算就是Java中的[方法引用],[方法引用]的格式是 类名::方法名
一般是用作Lambda表达式

例如:
表达式

person -> person.getName();

可以替换成:

Person::getName

表达式

() -> new HashMap<>();

可以替换成:

HashMap::new

这种[方法引用]或者说[双冒号运算]对应的参数类型是Function<T,R> T表示传入类型,R表示返回类型。

注解

类型注解
重复注解
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值