Java11 前两天都发布了, Java8 还没搞清都有啥, 这就很尴尬了~
Java8 的新特性: Lambda 表达式, 强大的 Stream API, 全新时间日期 API,ConcurrentHashMap,MetaSpace. 总得来说, Java8 的新特性使 Java 的运行速度更快, 代码更少(Lambda 表达式), 便于并行, 最大化减少空指针异常.
本篇博客将以笔者的粗鄙见解去帮助还没接触 Java8 新特性的同学快速理解 lambda 表达式和 Stream API.
一: lambda
1. 引言
在 IDE 中, 你是否遇到过在写下列代码时, 代码出现灰色的情况:
- new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("thread");
- }
- });
这时候, 我们按一下快捷键, IDE 自动帮我们把代码优化为这个样子:
new Thread(() -> System.out.println("thread"));
其实这就是 Java8 的新特性: lambda 表达式.
2.lambda 表达式
借用引言中的示例, 在调用 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 表达式还有两种简化代码的手段, 它们是方法引用, 构造引用.
方法引用是什么呢? 如果我们要实现接口的方法与另一个方法 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;
也就是 "类:: 实例方法" 的形式.
构造引用
先来创建一个供给型接口对象:
Supplier<String> supplier = () -> new String();
在这个 lammbda 表达式中只做了一件事, 就是返回一个新的 Test 对象, 所以这种形式可以更简化:
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;
3.lambda 总结
上面并没有给出太多的 lambda 实例, 只是侧重讲了如何去理解 lambda 表达式. 到这里, 不要懵. 要记住 lambda 的本质: 为函数型接口的匿名实现进行简化与更简化.
所谓的简化就是 lambda 的标准形式, 所谓的更简化是在标准形式的基础上进行方法引用和构造引用.
方法引用是拿已有的方法去做实现现在的接口.
构造引用简化了只有一句 new Object()的代码形式.
二: Stream
在我看来, 学习 lambda 与学习 Stream 的联系就是因为在许多博客, 文档中对 Stream API 的讲解大量使用 lambda 表达式, 导致不学 lambda 表达式看不懂 Stream API.
1. 如何理解 Stream
Stream 不是集合元素, 它不是数据结构并不保存数据, 它是有关算法和计算的, 它更像一个高级版本的 Iterator. 简单来说, 它的作用就是通过一系列操作将数据源 (集合, 数组) 转化为想要的结果.
2.Stream 特点
Stream 是不会存储元素的.
Stream 不会改变原对象, 相反, 他们会返回一个持有结果的新 Stream.
Stream 操作是延迟执行的. 意味着它们会等到需要结果的时候才执行.
3. 生成 Stream 的方式
- //Collection 系的 stream() 和 parallelStream();
- List<String> list = new ArrayList<>();
- Stream<String> stream = list.stream();
- Stream<String> stringStream = list.parallelStream();
- // 通过 Arrays
- Stream<String> stream1 = Arrays.stream(new String[10]);
- // 通过 Stream
- Stream<Integer> stream2 = Stream.of(1, 2, 3);
- // 无限流
- // 迭代
- Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2);
- iterate.limit(10).forEach(System.out::println);
- // 生成
- Stream<Double> generate = Stream.generate(() -> Math.random());
- generate.forEach(System.out::println);
4.Stream 的中间操作
多个中间操作连接而成为流水线, 流水线不遇到终止操作是不触发任何处理的, 又称为 惰性求值.
- 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 遍历. 其它的几个操作思想都差不多.
执行上面的方法没什么用, 因为缺少终止操作.
5.Stream 的终止操作
- 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); // 归约
- System.out.println(reduce);
- Optional<Integer> reduce1 = list.stream()
- .map(s -> (s + 1))
- .reduce((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());
其它的用法参考下源码, 再看看 API 文档.
来源: https://www.cnblogs.com/yueshutong/p/9735157.html