Java16已经横空出世了,大家现在用的哪一版呢?不出所料,大多数都还在用Java8。
Oracle JDK 开始对 Java SE 8(8u201/202) 之后的版本进行商用收费,也就是说后续版本的JDK不再开源了,所以开源社区紧跟着推出了OpenJDK,MariaDB(主要是担心Oracle把Mysql也闭源了)等替代备选方案。
一是JDK可能要面临闭源,二是基于Java8的J2EE服务太庞大了,更换JDK不单单是要承受巨大的业务代码脱胎换骨般重构升级成本,还要承担JDK升级不可预知的安全稳定性风险。
最新的未必就是最好的最实用的!这也就是Java8持续盛行至今的一些原因吧。
既然大家都还在用Java8,那就简单说说Java8的一些常用特性。
1. Lambda表达式
JDK8之前繁琐的匿名内部类,现在可以通过Lambda表达式(也称闭包)简洁高效地编码实现,也称函数式编程。
orderDetails.forEach(orderDetail -> orderDetail.setOrderId(order.getId()));
上面示例的代码可以显示指定参数类型,如果Lambda表达式的函数体超过一行代码,可以加上花括号,如下:
orderDetails.forEach((OrderDetail orderDetail) -> {
orderDetail.setOrderId(order.getId());
orderDetail.setCreateTime(order.getCreateTime());
});
Lambda表达式,现在前后端开发基本都在用,前端有的叫箭头函数。
2. 函数式接口
带有 @FunctionalInterface 注解的接口类为函数式接口,仅起标记的作用,
可以隐式转换为Lambda表达式。
函数式接口:只有一个普通函数的接口(默认方法及静态方法除外)。
具有代表性的函数式接口有:
java.util.function.Function // 函数方法
java.util.function.Consumer // 消费者
java.util.function.Supplier // 提供者
java.util.function.Predicate // 断言判断
应用示例:
public static <D extends Domain> Map<Integer, D> listToMap(List<D> resources){
if(CollectionUtils.isNotEmpty(resources)){
return resources.stream().collect(
Collectors.toMap(D::getId, Function.identity(), (key1, key2) -> key2));
}
return Collections.emptyMap();
}
Function.identity() // 返回对象本身
(key1, key2) -> key2 // 表示当map的key重复时,value取最新值,即覆盖处理
3. 接口默认方法及静态方法
JDK8中对接口的功能做了扩展,以往只支持定义抽象方法,现在可以定义default修饰的默认方法及static修饰的静态方法,下面各自举例说明一下。
默认方法:
默认方法的功能可以被子类直接继承使用,如果不能满足子类功能,子类可以覆写默认方法。
public interface BaseRepository<S extends Domain> extends JpaSpecificationExecutor<S>, JpaRepository<S, Integer> {
default Specification<S> getSpecification(S example) {
return (root, criteriaQuery, criteriaBuilder) -> {
Path<Integer> id = root.get("id");
Path<Date> createTime = root.get("createTime");
Path<Byte> delFlag = root.get("delFlag");
List<Predicate> predicateList = new ArrayList<>();
if (example.getId() != null && example.getId() > 0) {
predicateList.add(criteriaBuilder.equal(id, example.getId()));
}
if (example.getDelFlag() != null) {
predicateList.add(criteriaBuilder.equal(delFlag, example.getDelFlag()));
}
if (StringUtils.isNotEmpty(example.getFromDate())) {
predicateList.add(criteriaBuilder.greaterThanOrEqualTo(createTime, DateUtil.parseYMDHMS(example.getFromDate() + " 00:00:00")));
}
if (StringUtils.isNotEmpty(example.getToDate())) {
predicateList.add(criteriaBuilder.lessThanOrEqualTo(createTime, DateUtil.parseYMDHMS(example.getToDate() + " 23:59:59")));
}
return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
};
}
}
静态方法:
子类可以直接调用,不能覆写
@FunctionalInterface
public interface Function<T, R> {
static <T> Function<T, T> identity() { return t -> t; }
}
4. 方法引用
HashMap<Integer, Set<Integer>> userActionMap = userActionList.stream().collect(
Collectors.groupingBy(UserAction::getBizId, HashMap::new, Collectors.mapping(UserAction::getUserId, Collectors.toSet())))
UserAction::getBizId // 成员方法引用
HashMap::new // 无参构造器引用,分组后用HashMap作为数据结果收集器
Collectors.groupingBy // 分组聚合,如本例的按userAction.bizId字段分组
Collectors.mapping // 分组数据的value值映射处理,如本例取userAction.userId,存放到set集合
分组排序应用示例:
public static Map<String, Long> tokenize(String content){
if(StringUtils.isNotEmpty(content)){
List<String> tokens = process(content);
Map<String, Long> items = tokens.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
return items.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));
}
return Collections.emptyMap();
}
5. Stream 流式操作
List<List<User>> userList = getUserByGroup();
Predicate<User> predicate = user -> StringUtils.isNotEmpty(user.getUserName());
userList.stream().filter(predicate)
.flatMap(Collection::stream).collect(Collectors.toList())
一些常用API说明:
- filter:过滤
- map:将函数转为其他流或提取信息
- mapToInt:提取值,并转为int类型;如:userList.mapToLong(User::getScore).sum();
- flatMap:返回一个stream,flatMap将流中的当前元素替换为此返回流拆解的流元素,如本例将List<List<User>> 双重集合拆分成扁平化流,流元素即为User对象
- distinct:去重复
- sorted:排序
- limit:取前几个;userList.stream().limit(10).collect(Collectors.toList());
- skip:跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。如:userList.stream().skip(10).collect(Collectors.toList());
- collect:接口中的方法,定义了如何对流结果进行操作:如:.collect(Collectors.toList())
- anyMatch:检查至少一个匹配(返回类型boolean)
- noneMatch:检查是否没有匹配的元素
- findFirst:返回第一个结果(返回类型:Optional)
- findAny:返回任意个结果
组合排序问题:
ComparaUser> comparator =
Comparator.comparing(User::getScore, Comparator.nullsLast(Double::compareTo))
.thenComparing(User::getUpdateTime, Comparator.nullsLast(Date::compareTo))
.thenComparing(User::getId).reversed();
Comparator.nullsLast 即空值元素排最后,reversed()表示逆序排列。
以上排序规则为:先按用户积分倒序,积分相同的,按用户更新时间倒序,更新时间也相同的,再按用户ID倒序。
上述例子中,reversed()方法是作用于全字段的,即score,updateTime,id 都倒序,如果在score或updateTime排序后再加上reversed()方法则为按score或updateTime升序排列(类似于负负得正)
未完待续。。。。。。