大概一年多之前, 我对 java8 的理解还仅限一些只言片语的文章之上, 后来出于对函数式编程的兴趣, 买了本参考书看了一遍, 然后放在了书架上, 后来, 当我接手大客户应用的开发工作之后, java8 的一些工具, 对我的效率有了不小的提升, 因此想记录一下 java'8 的一些常用场景, 我希望这会成为一个小字典, 能让我免于频繁翻书, 但是总能找到自己想找的知识.
用于举例的 model:
- @Data
- public class Apple {
- private Long appleId;
- private String appleName;
- private Float appleWeight;
- private Integer appleClassic;
- }
一, Java 8 Lambda 表达式
这无疑是最常用的功能之一, 其实 lambda 表达式的作用, 应该就是简洁明了, 实际上是用最短的字符, 通过类型推导, 语法糖等方式去对编译器描述清楚这段代码的功能, 这和泛型有点相似, 对于编程人员来说, 一定程度上也提高了编程效率和代码可读性.
如常用的 lambda 表达式: process(()->System.out.println("this is so cool!"))
例如对苹果重量排序:
- List<Apple> apples = Lists.newArrayList();
- for (int i = 1; i <10; i++) {
- Apple apple = new Apple();
- apples.add(apple);
- }
- apples.sort(Comparator.comparing(Apple::getAppleWeight));
反序:
apples.sort(Comparator.comparing(Apple::getAppleWeight).reversed());
重量相同时: 比较等级:
- apples.sort(Comparator
- .comparing(Apple::getAppleWeight)
- .reversed()
谓词复合查询:
- Predicate<Apple> a = apple -> apple.getAppleWeight()> 10;
- weight10.or(apple -> apple.getAppleClassic()> 2)
- .and(apple -> StringUtils.equalsIgnoreCase(apple.getAppleName(), "优质苹果"));
可以看做 (a||b)&&c
函数复合:
- Function<Apple, Float> f = a -> a.getAppleWeight() + 1;
- Function<Float, Float> g = a -> a * 2;
- Function<Apple, Float> h = f.andThen(g);
数学写作 h=g(f(x))
- Function<Apple, Float> g = a -> a.getAppleWeight() + 1;
- Function<Float, Float> f = a -> a * 2;
- Function<Apple, Float> h = f.compose(g);
数学写作 h=f(g(x))
小结: java8 实际上想传递函数, 函数是什么? 是一个映射, 可以看做 x->y, 输入 x 然后映射到值 y 的过程, java 无法摆脱一切皆是对象的思想, 因此函数式依附在对象上传递的, 因此也有了下面的说法, 方法引用, 以及函数式接口, 让函数随着对象传递, 为了函数式编程, 甚至专门写一个接口 --- 对象来传递函数. 然而, 函数才是主角.
二, Java 8 方法引用
方法引用十分简单, 其实也是将方法作为参数传递. 使用:: 域作用符, 将一段方法传递. 举例: Apple::getAppleId String::subString System.out::println
三, Java 8 函数式接口 函数式编程
利用 java 进行函数式编程主要就是利用函数式接口, 但是函数式接口在 java8 之前就有一些了, 就例如多线程的 runnable, 但是 8 以前是没有 lambda 表达式的, 所以只能使用匿名内部类, 在用过 lambda 表达式的人看来, 那是相当臃肿的, 8 更新了 lambda 表达式, 这就使函数式编程更上一层楼.
java8 的函数式接口为我们传递函数提供了工具, 我们可以自己定义函数式接口, 然后让其他人, 或者是 java API 调用. 关于函数接口, 需要记住的就是两件事: 函数接口是行为的抽象; 函数接口是数据转换器.
四, Java 8 Stream
在我接触到 java8 流式处理的时候, 我的第一感觉是流式处理让集合操作变得简洁了许多, 通常我们需要多行代码才能完成的操作, 借助于流式处理可以在一行中实现. 其本质是, 将一些原本开发者需要做的处理如迭代等, 放在了 java 库里, 让我们只关心自己的业务逻辑, 比如我们希望对一个包含整数的集合中筛选出所有的偶数, 并将其封装成为一个新的 List 返回, 那么在 java8 之前, 我们需要通过如下代码实现:
过去:
- List<Integer> evens = new ArrayList<>();
- for (final Integer num : nums) {
- if (num % 2 == 0) {
- evens.add(num);
- }
- }
stream 实现:
List<Integer> evens = nums.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());
我们需要取出 10 个等级高于 3 的苹果, 跳过其中两个, 按重量排序, 去重, 然后取出苹果的 Name, 然后取出名字的每个字符:
- List<String> appleName = apples.parallelStream()
- .filter(a -> a.getAppleClassic() <2)
- .sorted(Comparator.comparing(Apple::getAppleWeight))
- .map(Apple::getAppleName)
- .map(s -> s.split(""))
- .limit(10)
- .skip(2)
- .distinct()
- .flatMap(Arrays::stream)
- .collect(Collectors.toList());
构造 AppleId ApppleName Map:
- Map<Long, String> appleIdMap = apples.stream()
- .collect(Collectors.toMap(Apple::getAppleId, Apple::getAppleName, (s, s2) -> s.length()> s2.length() ? s : s2));
谓词查找:
- if (appleName.stream().anyMatch(a -> StringUtils.equalsIgnoreCase(a, "一级苹果")));
- if (appleName.stream().allMatch(a -> StringUtils.equalsIgnoreCase(a, "一级苹果")));
- if (appleName.stream().noneMatch(a -> StringUtils.equalsIgnoreCase(a, "一级苹果")));
短路查找:
- appleName.stream()
- .filter(a -> StringUtils.equalsIgnoreCase(a, "一级苹果"))
- .findAny()
- .ifPresent(System.out::println);
findfirst 在并行时限制多一些, 如果不在意返回的是哪个元素, 使用 findAny.
求和:
- apples.stream()
- .map(Apple::getAppleWeight)
- .reduce(0F, (a, b) -> a + b);
计数:
apples.stream().count();
使用 stream 的好处: 1. 更简洁, 更易读 2. 可复合, 更灵活 3. 可并行
五, Java 8 Optional 类
Optional 着重为解决 java 的 NPE 问题是 Java8 提供的为了解决 null 安全问题的一个 API. 善用 Optional 可以使我们代码中很多繁琐, 丑陋的设计变得十分优雅.
使用 Optional, 我们就可以把下面这样的代码进行改写:
- public static String getName(User u) {
- if (u == null)
- return "Unknown";
- return u.name;
- }
不过, 千万不要改写成这副样子.
- public static String getName(User u) {
- Optional<User> user = Optional.ofNullable(u);
- if (!user.isPresent())
- return "Unknown";
- return user.get().name;
- }
这样才是正确使用 Optional 的姿势. 那么按照这种思路, 我们可以安心的进行链式调用, 而不是一层层判断了.
- public static String getName(User u) {
- return Optional.ofNullable(u)
- .map(user->user.name)
- .orElse("Unknown");
- }
看一段代码:
- public static String getChampionName(Competition comp) throws IllegalArgumentException {
- if (comp != null) {
- CompResult result = comp.getResult();
- if (result != null) {
- User champion = result.getChampion();
- if (champion != null) {
- return champion.getName();
- }
- }
- }
- throw new IllegalArgumentException("The value of param comp isn't available.");
- }
让我们看看经过 Optional 加持过后, 这些代码会变成什么样子.
- public static String getChampionName(Competition comp) throws IllegalArgumentException {
- return Optional.ofNullable(comp)
- .map(c->c.getResult())
- .map(r->r.getChampion())
- .map(u->u.getName())
- .orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available."));
- }
还有很多不错的使用姿势, 比如为空则不打印可以这么写:
string.ifPresent(System.out::println);
参考资料:《Java 8 in Action: Lambdas, streams, and functional-style programming》 Raoul-gabriel Urma (作者), Mario Fusco (作者), Alan Mycroft (作者)
作者: 文烁 点击 阅读更多 https://tech.antfin.com/articles?chInfo=zh 查看更多详情
来源: https://juejin.im/post/5c47d211e51d45263c1d6e6c