欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java8的lambda表达式和Stream API

程序员文章站 2022-06-07 20:33:28
一直在用JDK8 ,却从未用过Stream,为了对数组或集合进行一些排序、过滤或数据处理,只会写for循环或者foreach,这就是我曾经的一个写照。 刚开始写写是打基础,但写的多了,各种乏味,非过来人不能感同身受。今天,我就要分享一篇如何解决上述问题的新方法 - Stream API。但学习Str ......

一直在用jdk8 ,却从未用过stream,为了对数组或集合进行一些排序、过滤或数据处理,只会写for循环或者foreach,这就是我曾经的一个写照。

 

刚开始写写是打基础,但写的多了,各种乏味,非过来人不能感同身受。今天,我就要分享一篇如何解决上述问题的新方法 - stream api。但学习stream之前却不得不学一下lambda表达式。说实话,网上介绍lambda表达式的文章很多,大多晦涩难懂,今天我就想用自己的理解去说一下lambda表达式是如何让我们的代码写的更少!

 

 

02来自idea的提示

 

 

在ide中,你是否遇到在写以下列代码时,被友情提示的情况:

new thread(new runnable() {
@override
public void run() {
system.out.println("thread");
}
});

这时候,我们按一下快捷键,ide自动帮我们把代码优化为酱个样子:

new thread(() -> system.out.println("thread"));

这就是java8的新特性:lambda表达式。

 

03lambda入门

 

 

借用上面的示例,在调用new thread的含参构造方法时,我们通过匿名内部类的方式实现了runnable对象,但其实有用的代码只有system.out.println("thread")这一句,而我们却要为了这一句去写这么多行代码。正是这个问题,才有了java8中的lambda表达式。那lambd表达式究竟是如何简化代码的呢?

 

先来看lambda表达式的语法:

() -> {}

(): 括号就是接口方法的括号,接口方法如果有参数,也需要写参数。只有一个参数时,括号可以省略。

->: 分割左右部分的,没啥好说的。

{} : 要实现的方法体。只有一行代码时,可以不加括号,可以不写return。

 

看了上面的解释,也就不难理解ide优化后的代码了。不过看到这里你也许意识到,如果接口中有多个方法时,按照上面的逻辑lambda表达式恐怕不行了。没错,lambda表达式只适用于函数型接口。说白了,函数型接口就是只有一个抽象方法的接口。这种类型的接口还有一个对应的注解:@functionalinterface。为了让我们在需要这种接口时不再自己去创建,java8中内置了四大核心函数型接口。

 

消费型接口(有参无返回值)

consumer<t>

void accept(t t);

供给型接口(无参有返回值)

supplier<t>

t get();

函数型接口(有参有返回值)

function<t, r>

r apply(t t);

断言型接口(有参有布尔返回值)

predicate<t>

boolean test(t t);

 

看到这里如果遇到一般的lambda表达式,你应该可以从容面对了,但高级点的恐怕看到还是懵,不要急,其实也不难。lambda表达式还有两种简化代码的手段,分别是方法引用构造引用

 

 

04方法引用

 

 

方法引用是什么呢?如果我们要实现接口的方法与另一个方法a类似,(这里的类似是指参数类型与返回值部分相同),我们直接声明a方法即可。也就是,不再使用lambda表达式的标准形式,改用高级形式。无论是标准形式还是高级形式,都是lambda表达式的一种表现形式。

举例:

function function1 = (x) -> x;
function function2 = string::valueof;

对比function接口的抽象方法与string的value方法,可以看到它们是类似的。

r apply(t t);

public static string valueof(object obj) {
return (obj == null) ? "null" : obj.tostring();
}

方法引用的语法:

对象::实例方法
类::静态方法
类::实例方法

前两个很容易理解,相当于对象调用实例方法,类调用静态方法一样。只是第三个需要特殊说明。

当出现如下这种情况时:

compare<boolean> c = (a, b) -> a.equals(b);

用lambda表达式实现compare接口的抽象方法,并且方法体只有一行,且该行代码为参数1调用方法传入参数2。此时,就可以简化为下面这种形式:

compare<boolean> c = string::equals;

也就是“类::实例方法”的形式。

值得一提的是,当参数b不存在时,该方式依旧适用。例如:

function function1 = (x) -> x.tostring();
function function1 = object::tostring;

 

 

05构造引用

 

 

先来创建一个供给型接口对象:

supplier<string> supplier = () -> new string();

在这个lammbda表达式中只做了一件事,就是返回一个新的string对象,而这种形式可以更简化:

supplier<string> supplier = string::new;

提炼一下构造引用的语法:

类名::new

当通过含参构造方法创建对象,并且参数列表与抽象方法的参数列表一致,也就是下面的这种形式:

function1 function = (x) -> new string(x);

也可以简化为:

function1 function = string::new;

特殊点的数组类型:

function<integer,string[]> function = (x) -> new string[x];

可以简化为:

function<integer,string[]> function = string[]::new;

 

 

06lambda总结

 

 

上面并没有给出太多的lambda实例,只是侧重讲了如何去理解lambda表达式。到这里,不要懵。要记住lambda的本质:为函数型接口的匿名实现进行简化与更简化。

所谓的简化就是lambda的标准形式,所谓的更简化是在标准形式的基础上进行方法引用和构造引用。

方法引用是拿已有的方法去实现此刻的接口。

构造引用是对方法体只有一句new object()的进一步简化。

 

 

07stream理解

 

 

如何理解stream?在我看来,stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 iterator。简单来说,它的作用就是通过一系列操作将数据源(集合、数组)转化为想要的结果。

 

stream有三点非常重要的特性:

  1. stream 是不会存储元素的。

  2. stream 不会改变原对象,相反,他们会返回一个持有结果的新stream。

  3. stream 操作是延迟执行的。意味着它们会等到需要结果的时候才执行。

 

08stream生成

 

 

collection系的 stream() 和 parallelstream()

list<string> list = new arraylist<>();
stream<string> stream = list.stream();
stream<string> stringstream = list.parallelstream();

通过arrays.stram()

stream<string> stream1 = arrays.stream(new string[10]);

通过stream.of()

stream<integer> stream2 = stream.of(1, 2, 3);

通过stream.iterate()生成无限流

stream<integer> iterate = stream.iterate(0, (x) -> x + 2);
iterate.limit(10).foreach(system.out::println);

通过stream.generate()

stream<double> generate = stream.generate(() -> math.random());generate.foreach(system.out::println);

 

 

09stream中间操作

 

 

多个中间操作连接而成为流水线,流水线不遇到终止操作是不触发任何处理的,所为又称为“惰性求值”。

list.stream()
.map(s -> s + 1) //映射
.flatmap(s -> stream.of(s)) //和map差不多,但返回类型为stream,类似list.add()和list.addall()的区别
.filter(s -> s < 1000) //过滤
.limit(5) //限制
.skip(1) //跳过
.distinct() //去重
.sorted() //自然排序
.sorted(integer::compareto) //自定义排序

关于map方法,参数为一个function函数型接口的对象,也就是传入一个参数返回一个对象。这个参数就是集合中的每一项。类似iterator遍历。其它的几个操作思想都差不多。

执行上面的方法没什么用,因为缺少终止操作。

 

 

10stream的终止操作

 

 

常用的终止api如下:

list.stream().allmatch((x) -> x == 555); // 检查是否匹配所有元素
list.stream().anymatch(((x) -> x>600)); // 检查是否至少匹配一个元素
list.stream().nonematch((x) -> x>500); //检查是否没有匹配所有元素
list.stream().findfirst(); // 返回第一个元素
list.stream().findany(); // 返回当前流中的任意一个元素
list.stream().count(); // 返回流中元素的总个数
list.stream().foreach(system.out::println); //内部迭代
list.stream().max(integer::compareto); // 返回流中最大值
optional<integer> min = list.stream().min(integer::compareto);//返回流中最小值
system.out.println("min "+min.get());

reduce (归约):将流中元素反复结合起来得到一个值

integer reduce = list.stream()
.map(s -> (s + 1))
.reduce(0, (x, y) -> x + y); //归约:0为第一个参数x的默认值,x是计算后的返回值,y为每一项的值。
system.out.println(reduce);

optional<integer> reduce1 = list.stream()
.map(s -> (s + 1))
.reduce((x, y) -> x + y); // x是计算后的返回值,默认为第一项的值,y为其后每一项的值。
system.out.println(reduce);

collect(收集):将流转换为其他形式。需要collectors类的一些方法。

//转集合
set<integer> collect = list.stream()
    .collect(collectors.toset());

list<integer> collect2 = list.stream()
    .collect(collectors.tolist());

hashset<integer> collect1 = list.stream()
    .collect(collectors.tocollection(hashset::new));

//分组 {group=[444, 555, 666, 777, 555]}
map<string, list<integer>> collect3 = list.stream()
    .collect(collectors.groupingby((x) -> "group"));//将返回值相同的进行分组
system.out.println(collect3);

//多级分组 {group={777=[777], 666=[666], 555=[555, 555], 444=[444]}}
map<string, map<integer, list<integer>>> collect4 = list.stream()
    .collect(collectors.groupingby((x) -> "group", collectors.groupingby((x) -> x)));
system.out.println(collect4);

//分区 {false=[444], true=[555, 666, 777, 555]}
map<boolean, list<integer>> collect5 = list.stream()
    .collect(collectors.partitioningby((x) -> x > 500));
system.out.println(collect5);

//汇总
doublesummarystatistics collect6 = list.stream()
    .collect(collectors.summarizingdouble((x) -> x));
system.out.println(collect6.getmax());
system.out.println(collect6.getcount());

//拼接 444,555,666,777,555
string collect7 = list.stream()
    .map(s -> s.tostring())
    .collect(collectors.joining(","));
system.out.println(collect7);

//最大值
optional<integer> integer = list.stream()
    .collect(collectors.maxby(integer::compare));
system.out.println(integer.get());