5.4 一些细节
Lambda表达式的引入也推动了一些新方法被加入集合类。让我们来看看Map类的一些变化。
构建Map时,为给定值计算键值是常用的操作之一,一个经典的例子就是实现一个缓存。传统的处理方式是先试着从Map中取值,如果没有取到,创建一个新值并返回。
假设使用Map定义缓存,我们需要使用费时的数据库操作查询艺术家信息,代码可能如例5-31所示。
例5-31 使用显式判断空值的方式缓存
public Artist getArtist(String name) {Artist artist = artistCache.get(name);if (artist == null) {artist = readArtistFromDB(name);artistCache.put(name, artist);}return artist;}
Java 8引入了一个新方法computeIfAbsent,该方法接受一个Lambda表达式,值不存在时使用该Lambda表达式计算新值。使用该方法,可将上述代码重写为例5-32所示的形式。
例5-32 使用computeIfAbsent缓存
public Artist getArtist(String name) {return artistCache.computeIfAbsent(name, this::readArtistFromDB);}
你可能还希望在值不存在时不计算,为Map接口新增的compute和computeIfAbsent就能处理这些情况。
在工作中,你可能尝试过在Map上迭代。过去的做法是使用value方法返回一个值的集合,然后在集合上迭代。这样的代码不易读。例5-33展示了本章早些时候介绍的一种方式,创建一个Map,然后统计每个艺术家专辑的数量。
例5-33 一种丑陋的迭代Map的方式
Map<Artist, Integer> countOfAlbums = new HashMap<>();for(Map.Entry<Artist, List<Album>> entry : albumsByArtist.entrySet()) {Artist artist = entry.getKey();List<Album> albums = entry.getValue();countOfAlbums.put(artist, albums.size());}
谢天谢地,Java 8为Map接口新增了一个forEach方法,该方法接受一个BiConsumer对象为参数(该对象接受两个参数,返回空),通过内部迭代编写出易于阅读的代码,关于内部迭代请参考3.1节。使用该方法重写后的代码如例5-34所示。
例5-34 使用内部迭代遍历Map里的值
Map<Artist, Integer> countOfAlbums = new HashMap<>();albumsByArtist.forEach((artist, albums) -> {countOfAlbums.put(artist, albums.size());});
