8.1 集合工厂
如果我想创建一个集合,之前的做法是先new一个list,然后再一个个的add,这样子有点繁琐。
现在的方法可以这样,是使用 Arrays.asList()工厂方法:List<String> friends = Arrays.asList("Raphael", "Olivia", "Thibaut");
但是这样子创建只能更新,不可以增加、删除。
8.1.1 List 工厂
工厂方法 List.of方法,创建的是一个只读的列表。
它可以保护你的集合,以免被意外地修改。
建议是除非你需要进行某种形式的数据处理并对数据进行转换,否则应该尽量使用工厂方法。工厂方法使用起来更简单,实现也更容易,并且在大多数情况下就够用了。
8.1.2 Set 工厂
创建一个set集合 Set.ofSet<String> friends = Set.of("Raphael", "Olivia", "Thibaut");
8.1.3 Map 工厂
与list、set相比,创建map稍显复杂,因为你需要同时传递键和值。Java 9中提供了两种初始化一个不可变 Map 的方式。Map<String, Integer> ageOfFriends = Map.of("Raphael", 30, "Olivia", 25, "Thibaut", 26);
如果你只需要创建不到 10 个键值对的小型 Map,那么使用这种方法比较方便。
规模比较大的话,就使用Map.ofEntries 的工厂方法,该工厂方法接受以变长参数列表形式组织的 Map.Entry<K, V>对象作为参数
import static java.util.Map.entry;
Map<String, Integer> ageOfFriends
= Map.ofEntries(entry("Raphael", 30),
entry("Olivia", 25),
entry("Thibaut", 26));
System.out.println(ageOfFriends);
8.2 使用 List 和 Set
Java 8 在 List 和 Set 的接口中新引入了以下方法。
- removeIf 移除集合中匹配指定谓词的元素。实现了 List 和 Set 的所有类都提供了该方法(事实上,这个方法继承自 Collection 接口)。
- replaceAll 用于 List 接口中,它使用一个函数(UnaryOperator)替换元素。
- sort 也用于 List 接口中,对列表自身的元素进行排序。
8.2.1 removeIf 方法
循环的时候remove就会抛异常,ConcurrentModificationException。因为在底层实现上,for-each 循环使用了一个迭代器对象,集合由两个不同的对象管理着
一般解决这个问题就需要显示的调用Iterator 对象,并通过它调用 remove()方法
for (Iterator<Transaction> iterator = transactions.iterator();
iterator.hasNext(); ) {
Transaction transaction = iterator.next();
if(Character.isDigit(transaction.getReferenceCode().charAt(0))) {
iterator.remove();
}
}
不过这样子的话,代码就显得繁琐,所以就可以使用removeIf了,transactions.**removeIf**(transaction -> Character.isDigit(transaction.getReferenceCode().charAt(0)));
8.2.2 replaceAll 方法
List 接口提供的 replaceAll 方法让你可以使用一个新的元素替换列表中满足要求的每个元素。
原来的方式:
referenceCodes.stream()
.map(code -> Character.toUpperCase(code.charAt(0)) +
code.substring(1))
.collect(Collectors.toList())
.forEach(System.out::println);
输入: [a12, C14, b13]
输出: A12, C14, B13
或者
for (ListIterator<String> iterator = referenceCodes.listIterator(); iterator.hasNext(); ) {
String code = iterator.next();
iterator.set(Character.toUpperCase(code.charAt(0)) + code.substring(1));
}
现在可以这样子:
referenceCodes.replaceAll(code -> Character.toUpperCase(code.charAt(0)) + code.substring(1));
8.3 使用 Map
Java 8 在 Map 接口中新引入了几个默认方法
8.3.1 forEach 方法
使用 Map.Entry<K, V>迭代器访问 Map 集合中的每一个元素:
for(Map.Entry<String, Integer> entry: ageOfFriends.entrySet()) {
String friend = entry.getKey();
Integer age = entry.getValue();
System.out.println(friend + " is " + age + " years old");
}
从 Java 8 开始,Map 接口开始支持 forEach 方法,该方法接受一个 BiConsumer,以 Map的键和值作为参数。ageOfFriends.forEach((friend, age) -> System.out.println(friend + " is " + age + " years old"));
8.3.2 排序
Entry.comparingByValue 、Entry.comparingByKey这两个可以帮助map排序
favouriteMovies
.entrySet()
.stream()
.sorted(Entry.comparingByKey())
.forEachOrdered(System.out::println);
8.3.3 getOrDefault 方法
当获取的对象不存在的话,就返回一个默认值System.out.println(favouriteMovies.getOrDefault("Olivia", "Matrix"));
如果key为Olivia的值不存在,那么就返回"Matrix"
8.3.4 计算模式
就是如果key不存在,那么就执行某个操作
- computeIfAbsent——如果指定的键没有对应的值(没有该键或者该键对应的值是空),那么使用该键计算新的值,并将其添加到 Map 中;
- computeIfPresent——如果指定的键在 Map 中存在,就计算该键的新值,并将其添加到 Map 中;
- compute——使用指定的键计算新的值,并将其存储到 Map 中。
computeIfAbsent 的一个应用场景是缓存信息。
// 如果key不存在就执行calculateDigest操作
lines.forEach(line -> dataToHash.computeIfAbsent(line, this::calculateDigest));
还有这个场景,
String friend = "Raphael";
List<String> movies = friendsToMovies.get(friend);
if(movies == null) {
movies = new ArrayList<>();
friendsToMovies.put(friend, movies);
}
movies.add("Star Wars");
优化
friendsToMovies.computeIfAbsent("Raphael", name -> new ArrayList<>())
.add("Star Wars");
8.3.5 删除模式
java提供了一个重载方法的remove方法。
可能是这样子删除的:判断map的key存在,再比较值是否相等,然后再删除
String key = "Raphael";
String value = "Jack Reacher 2";
if (favouriteMovies.containsKey(key) &&
Objects.equals(favouriteMovies.get(key), value)) {
favouriteMovies.remove(key);
return true;
} else {
return false;
}
现在这样:favouriteMovies.remove(key, value);
vlaue小于10就删除movies.entrySet().removeIf(entry -> entry.getValue() < 10);
8.3.6 替换模式
Map 中提供了两种新的方法来替换其内部映射项,分别是:
- replaceAll——通过 BiFunction 替换 Map 中每个项的值。该方法的工作模式类似于之前介绍过的 List 的 replaceAll 方法;
- Replace——如果键存在,就可以通过该方法替换 Map 中该键对应的值。它是对原有replace 方法的重载,可以仅在原有键对应某个特定的值时才进行替换。
movie 就是valuefavouriteMovies.replaceAll((friend, movie) -> movie.toUpperCase());
8.3.7 merge 方法
Map<String, String> friends = Map.ofEntries(entry("Raphael", "Star Wars"));
Map<String, String> everyone = new HashMap<>(family);
everyone.putAll(friends);
System.out.println(everyone);
但是如果是有冲突的合并需要处理,可以这样
Map<String, String> family = Map.ofEntries(
entry("Teo", "Star Wars"), entry("Cristina", "James Bond"));
Map<String, String> friends = Map.ofEntries(
entry("Raphael", "Star Wars"), entry("Cristina", "Matrix"));
Map<String, String> everyone = new HashMap<>(family);
friends.forEach((k, v) ->
everyone.merge(k, v, (movie1, movie2) -> movie1 + " & " + movie2)); // 如果存在重复的键,就连接两个值
System.out.println(everyone);
merge 方法处理空值的方法相当复杂,在 Javadoc 文档中是这么描述的:
如果指定的键并没有关联值,或者关联的是一个空值,那么[merge]会将它关联到指定的非空值。否则,[merge]会用给定映射函数的[返回值]替换该值,如果映射函数的返回值为空就删除[该键]。
8.4 改进的 ConcurrentHashMap
并发安全,使用的是分段锁
8.4.1 归约和搜索
ConcurrentHashMap 类支持三种新的操作
- forEach——对每个(键, 值)对执行指定的操作;
- reduce——依据归约函数整合所有(键, 值)对的计算结果;
- search——对每个(键, 值)对执行一个函数,直到函数取得一个非空值。
每种操作支持四种形式的参数,接受函数使用键、值、Map.Entry 以及(键, 值)对作为参数:
- 使用键(forEachKey,reduceKeys,searchKeys);
- 使用值(forEachValue,reduceValues,searchValues);
- 使用 Map.Entry 对象(forEachEntry,reduceEntries,searchEntries);
- 使用键和值(forEach,reduce,search)。
ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>();
long parallelismThreshold = 1;
Optional<Integer> maxValue =
Optional.ofNullable(map.reduceValues(parallelismThreshold, Long::max));
8.4.2 计数
ConcurrentHashMap 类提供了一个新的 mappingCount 方法,能以长整形 long 返回 Map中的映射数目。你应该尽量在新的代码中使用它,而不是继续使用返回 int 的 size 方法。
文章来源:https://www.toymoban.com/news/detail-423207.html
8.4.3 Set 视图
ConcurrentHashMap 类还提供了一个新的 keySet 方法,该方法以 Set 的形式返回ConcurrentHashMap 的一个视图(Map 中的变化会反映在返回的 Set 中,反之亦然)。
文章来源地址https://www.toymoban.com/news/detail-423207.html
8.5 小结
- Java 9 支持集合工厂,使用 List.of、Set.of、Map.of 以及 Map.ofEntries 可以创建小型不可变的 List、Set 和 Map。
- 集合工厂返回的对象都是不可变的,这意味着创建之后你不能修改它们的状态。
- List 接口支持默认方法 removeIf、replaceAll 和 sort。
- Set 接口支持默认方法 removeIf。
- Map 接口为常见模式提供了几种新的默认方法,并降低了出现缺陷的概率。
- ConcurrentHashMap 支持从 Map 中继承的新默认方法,并提供了线程安全的实现。
到了这里,关于《Java8实战》第8章 Collection API 的增强功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!