《Java8实战》第8章 Collection API 的增强功能
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.of
Set<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 就是value
favouriteMovies.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 方法。
8.4.3 Set 视图
ConcurrentHashMap 类还提供了一个新的 keySet 方法,该方法以 Set 的形式返回ConcurrentHashMap 的一个视图(Map 中的变化会反映在返回的 Set 中,反之亦然)。
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 的增强功能相关推荐
- 《Java8实战》-第六章读书笔记(用流收集数据-01)
用流收集数据 我们在前一章中学到,流可以用类似于数据库的操作帮助你处理集合.你可以把Java 8的流看作花哨又懒惰的数据集迭代器.它们支持两种类型的操作:中间操作(如 filter 或 map )和终 ...
- 《Java8实战》第1章 Java 8、9、10 以及 11 的变化
如想了解 Oracle 公司对 JDK 的最新支持情况,请访问https://www.oracle.com/technetwork/java/java-se-supportroadmap.html. ...
- Java8实战 阅读二周目感想
Java8实战是我目前看过的写的水平最高的一本书,由浅入深,深入浅出,九浅一深. 之前大略的过了一遍,但是对于前几章的内容一直有点雾里看花的感觉. 又读了一遍,感觉有点新的感想. 一.其中1.2.1中 ...
- 《Java8实战》读书笔记10:组合式异步编程 CompletableFuture
<Java8实战>读书笔记10:组合式异步编程 CompletableFuture 第11章 CompletableFuture:组合式异步编程 11.1 Future 接口 (只是个引子 ...
- Java8实战学习笔记(三)——函数式数据处理
一.引入流 (一).引言 1.流是什么 流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现).可以看成遍历数据集的高级迭代器. 流可以透明地并行 ...
- [201604]Java8实战(陆明刚 劳佳 译)
==[201604]Java8实战(陆明刚 劳佳 译)== 第一部分 基础知识 第 1 章 为什么要关心 Java 8 1.1 Java 怎么还在变 1.1.1 Java 在编程语言生态系统中的位置 ...
- Java8实战学习笔记(四)——高效 Java 8 编程(一)
一.重构.测试和调试 (一).为改善可读性和灵活性重构代码 用更紧凑的方式描述程序的行为 -- Lambda表达式 将一个既有的方法作为参数传递给另一个方法 -- 方法引用 如何运用前几章介绍的Lam ...
- Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序
Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序 C#原本是用来编写Windows以及Windows Phone的应用程序.自从Xamarin问世后,C#的作用就发生了很大的变化. ...
- 《Java8实战》笔记汇总
<Java8实战>笔记(01):为什么要关心Java8 <Java8实战>笔记(02):通过行为参数传递代码 <Java8实战>笔记(03):Lambda表达式 & ...
最新文章
- TOJ4537: n阶行列式
- Codeforces#371 Div2
- 《Effective Java》第8章 通用程序设计
- BringWindowToTop(), SetForegroundWindow(), SetActiveWindow()
- Perl函数pack/unpack(二进制读写)
- python快速上手下载_初学者如何尽快上手python
- html桌面雪花,html5 canvas雪花形状在线生成器
- 2011年数据库大会纪行
- WPF 依赖属性详解【转】
- 缓存面试 - 为什么要用缓存?缓存使用不当会造成什么后果?
- Jmeter简单的登录压力测试(使用json发送post请求)
- [漏洞案例]thinkcmf 2.x从sql注入到getshell实战
- android 动画基础,Android 动画基础
- GOOGLE本地搜索
- 我是如何完美解决WIN10崩溃无法自动恢复启动问题的
- Foxit PDF SDK for Linux (C++ Library) 8.4.1 Crack
- 一个事物两个方面的对比举例_写compare contrast essay如何对比两个事物/人物
- 【js】js获取今日和昨日0点和23点59分59秒
- 阿正入门深度学习---从EM算法开始
- Solidworks如何打开swb文件