乐趣区

Java8实用技能

大概一年多之前,我对 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 (作者)
作者:文烁点击 阅读更多 查看更多详情

退出移动版