时代在进步、环境在变化。作为一门编程语言,java为了应对计算应用背景变化--多核和处理大型数据集(大数据),java8(2014年3月发布)提供了新的工具集Stream,使得开发者能够更方便地处理类似sql的分组、过滤、统计等功能。
java8核心新特性总结起来主要是3方面:
- Lambda(匿名函数)
- 流(Stream)
- 默认方法
1 Lambda(匿名函数)
在之前,为了实现对集合中数据排序、指定自定义排序标准,实现的代码总是比较啰嗦的,如下:对库存排序,指标是苹果的重量
Collections.sort(inventory, new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});
这里new一个对象(Comparator是接口,实际上是new实现类),由于我们只是排序的时候使用一次,没必要定义这个实现类并new对象,所以sort()方法中的实际上是匿名内部类的对象。
Lambda实际上就是为了解决这个问题,毕竟弄这么一串匿名内部类写那么一串,仅仅只用一次。那么使用java8提供的Lambda可以将代码改写为:
inventory.sort(comparing(Apple::getWeight));
库存对象inventory的sort方法是Collections的一个方法:
static <T> void sort(List<T> list, Comparator<? super T> c)
根据指定的比较器引起的顺序对指定的列表进行排序,Comparator<T>
是一个接口,要求泛型T是可比较对象类型。这样的写法简洁不少、功能完全一致,只是书写习惯可能一时间不适应(不过函数式编程在scala等编程语言上很常见)。
再来一个例子,筛选一个目录中的所有隐藏文件。
在java 8之前,必须要使用File的listFiles(FileFilter filter) 方法,接收FileFilter 类型过滤器,定义你需要过滤规则,返回File[]对象;同样,这个过滤器只是用一次,使用匿名内部类对象的方式比较好,因此常规的实现方式:
File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
public boolean accept(File file) {
return file.isHidden();
}
});
在java 8中,则可以进行重构,使用如下方式实现:
File[] hiddenFiles = new File(".").listFiles(File::isHidden);
listFiles()方法接收的过滤器由File的方法引用File::isHidden提供,与java 8之前的方法接收值/对象不同,这里接收的方法,这也是函数式编程和面向对象编程区别所在:传递的对象不限于值/对象,传递的是方法。
Java 8中的主要变化反映了它开始远离侧重改变现有值的经典面向对象思想,向函数式编程领域转变。
2 流处理
Java 8在java.util.stream中添加了一个Stream API,Stream API的很多方法可以链接起来形成一个复杂的流水线,使得用户可以在一个更高的抽象层次上写Java程序:思路变成了把这样的流变成那样的流(如过滤什么在按统计什么指标等等);另一个方面,Java 8可以透明地把输入的不相关部分拿到几个CPU内核上去分别执行Stream操作流水线,用并行方式提升效率而无需额外编写Thread的方式。
相举个例子:从一个列表中筛选金额较高的交易,然后按货币分组。java 8前代码书写如下:
Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();
for (Transaction transaction : transactions) {
if(transaction.getPrice() > 1000){
Currency currency = transaction.getCurrency();
List<Transaction> transactionsForCurrency = transactionsByCurrencies.get(currency);
if (transactionsForCurrency == null) {
transactionsForCurrency = new ArrayList<>();
transactionsByCurrencies.put(currency, transactionsForCurrency);
}
transactionsForCurrency.add(transaction);
}
}
看起来一堆控制指令,完成数据筛选、统计功能,如果使用java 8则可以变为:
Map<Currency, List<Transaction>> transactionsByCurrencies = transactions.stream()
.filter((Transaction t) -> t.getPrice() > 1000)
.collect(groupingBy(Transaction::getCurrency));
不在用for-each循环一个个去迭代元素,然后再处理元素、汇总元素,而是使用Stream API,不在操心循环迭代的事情,直接在库内部进行数据处
理的。
3 默认方法
默认方法,能够达到往接口里面增加新的功能,且保持与老版本代码兼容(java8库设计者无法控制Collections所有现有的实现)。
也就是说,默认方法位于接口中,有具体的实现,不会强制具体类来现。这打破了曾经接口中不能含有方法实现的约束,破除了接口只是定义规则的出发点。
但是这种改变确实能够给编程者带来极大的方便,多数情况下使用默认的这个接口提供的方法,需要改变时再进行方法重载。Java 8要充分利用Lambda,需要增强大量的类库,但是又希望满足兼容性,也就想出了默认方法这个解决方案。
例如,在Java 8里,你现在可以直接对List调用sort方法。它是用Java 8 List接口中如下所示的默认方法实现的,它会调用Collections.sort静态方法:
default void sort(Comparator<? super E> c) {
Collections.sort(this, c);
}
这意味着List的任何实体类都不需要显式实现sort,而在以前的Java版本中,除非提供了sort的实现,否则这些实体类在重新编译时都会失败。