美文网首页
Lambda表达式入门

Lambda表达式入门

作者: AnimoBlog | 来源:发表于2020-08-25 16:34 被阅读0次

“Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。

函数接口

顾名思义何为函数接口和普通接口又有什么区别?
自JDK8以来接口可以有多个默认实现方法(普通接口)。

普通接口

普通接口上增加@FunctionalInterface即标识为函数接口,与其普通接口的区别在于函数接口只能有一个抽象方法,多个会报错

函数接口

JDK8为我们提供了很多函数接口在java.util.function包下。

函数接口包

函数接口给lambda带来了什么?
根据开头介绍Lambda是一个匿名函数,何为匿名函数?我们常见的匿名函数出现在Swing比较多,线程大家也知道举个例子。

public static void main(String[] args) {
        Thread thread = new Thread(()-> System.out.println("123123"));
        thread.start();
    }

这段代码很简单我们创建了个Thread,这里我们使用了需要Runnable接口的构造函数,为什么我们能使用Lambda表达式呢?因为Runnable接口遵循了函数接口设计规则。

Thread类 Runnable方法

这里我们可以看到Runnable函数接口只有一个方法然而方法没有形参所以我们可以通过()->逻辑代码这种形式去抒写。

Lambda

这里我们通过对集合的练习Lambda让大家有更深入的了解。

public class Transaction {
    private final Trader trader;
    private final int year;
    private final int value;
    public Transaction(Trader trader, int year, int value) {
        this.trader = trader;
        this.year = year;
        this.value = value;
    }
    public Trader getTrader() {
        return trader;
    }
    public int getYear() {
        return year;
    }
    public int getValue() {
        return value;
    }
}
class Trader{
    private final String name;
    private final String city;
    public String getName() {
        return name;
    }
    public String getCity() {
        return city;
    }
    public Trader(String name, String city) {
        this.name = name;
        this.city = city;
    }
}

创建集合数据。

Trader raoul = new Trader("Raoul", "Cambridge");
        Trader mario = new Trader("Mario","Milan");
        Trader alan = new Trader("Alan","Cambridge");
        Trader brian = new Trader("Brian","Cambridge");
        List<Transaction> transactions = Arrays.asList(
                new Transaction(brian, 2011, 300),
                new Transaction(raoul, 2012, 1000),
                new Transaction(raoul, 2011, 400),
                new Transaction(mario, 2012, 710),
                new Transaction(mario, 2012, 700),
                new Transaction(alan, 2012, 950)
        );

1、找出2011年发生的所有交易,并按交易额排序(从低到高)。

List<Transaction> list1 = transactions.stream().
filter(e->e.getYear()==2011).
sorted(Comparator.comparing(Transaction::getValue)).
collect(Collectors.toList());

首先通过transactions获取Stream API流,这里需要找出2011年所发生的的所有交易所以我们这里使用filter过滤结果集,下方是filter方法源码,可以看到需要的入参是一个函数接口,这里就可以采用Lambda。

Stream<T> filter(Predicate<? super T> predicate);

Predicate函数接口默认方法test 入参是一个泛型T,所以我们可以传递Transaction对象,返回参数是一个boolean类型,我们可以采用两种形式写Lambda可以隐式也可以显式,为了方便采用显示。
在filter之后的list已经被过滤了只有2011年的数据,又需要排序这里采用sorted,方法入参又是一个函数接口。

Stream<T> sorted(Comparator<? super T> comparator);源码
sorted(Comparator.comparing(Transaction::getValue))代码

入参我们采用了静态方法默认实现了里面的排序,静态方法入参是一个Function函数接口。

结果集

入参我们采用了语法糖Transaction::getValue = e->e.getValue(),综上所诉此阶段我们已经把2011排序之后的数据组装完成,这时候需要用到collect把组装的数据转换成集合再次输出到结果集这里就完成了筛选和排序,其实lambda和流水线差不多把需要做的事情一步一步做完之后再输出为成品。

2、交易员都在哪些不同的城市工作过

Set<String> list2 = transactions.stream().
map(e->e.getTrader().getCity()).
collect(Collectors.toSet());

map主要作用是获取对象中的特定属性,因为问题中的不同,所以我们采用toSet方法来去重,也可以采用distinct()在toList。

3、查找所有来自于剑桥的交易员,并按姓名排序

Set<Trader> list3 = transactions.stream().
map(e->e.getTrader()).
filter(e->e.getCity().equals("Cambridge")).
sorted(Comparator.comparing(Trader::getName)).
collect(Collectors.toSet());

因为Trader在Transaction所以我们需要用map把Trader抽取出来之后再进行城市过滤,过滤之后把名字排序在去重相同名字。

4、返回所有交易员的姓名字符串,按字母顺序排序

String list4 = transactions.stream().
map(e->e.getTrader().getName()).
distinct().
sorted().
reduce("",(a,b)->a+b);

通过map把所有交易员的姓名抽取出来,之后去重在通过排序默认从小到大,reduce方法就是把两个值结合起来生成新的值。
此方法效率不高因为每次迭代都会产生新的String,下方为优化代码。

tring traderStr = transactions.stream()
.map(transaction -> transaction.getTrader().getName())
.distinct()
.sorted()
.collect(joining());

5、有没有交易员是在米兰工作的

boolean list5 = transactions.stream().
anyMatch(e->e.getTrader().getCity().equals("Milan"))

anyMatch入参是一个Predicate函数接口和第一题中的filter入参一样,那为什么不用filter,这里出现个问题为什么不使用filter其效果也是一样的呀?
filter属于中间操作可以链接起来,将一个流转换成另一个流。这个操作不会去消耗流其目的是创建新的流所以还在"流水线"并未将其结果返回。与其相反,终端操作会消耗流,以产生一个最终结果,它们通常可以优化"流水线"来缩短计算时间。

6、打印生活在剑桥的交易员的所有交易额

transactions.stream().
filter(e->"Cambridge".equals(e.getTrader().getCity())).
map(Transaction::getValue).
forEach(System.out::println);

过滤出在剑桥的交易员,通过map提取出交易额在循环打印。

7、所有交易中,最高的交易额是多少

Optional<Integer> integer =  transactions.stream().
map(Transaction::getValue).
reduce(Integer::max);

这里返回值是用Optional包含的,JKD8加入的Optional预防空指针。

8、找到交易额最小的交易

Optional<Transaction> transaction = transactions.stream().
reduce((a,b)->a.getValue()<b.getValue()?a:b);

9、附加:交易额求和

int value = transactions.stream().
mapToInt(Transaction::getValue).sum();

以上就是一些基础的Lambda表达式,重要的不是去使用而是去理解该如何写,了解了函数接口之后再通过一系列的语法糖,也能随心所欲的写出Lambda,其次就是jdk自带的函数接口api比较多,用的时候还需百度,更多关于函数接口和Lambda的高级使用可以购买JDK8实战书籍。

博客链接:http://www.ljyanimo.com/

相关文章

网友评论

      本文标题:Lambda表达式入门

      本文链接:https://www.haomeiwen.com/subject/hnhzjktx.html