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 的增强功能相关推荐

  1. 《Java8实战》-第六章读书笔记(用流收集数据-01)

    用流收集数据 我们在前一章中学到,流可以用类似于数据库的操作帮助你处理集合.你可以把Java 8的流看作花哨又懒惰的数据集迭代器.它们支持两种类型的操作:中间操作(如 filter 或 map )和终 ...

  2. 《Java8实战》第1章 Java 8、9、10 以及 11 的变化

    如想了解 Oracle 公司对 JDK 的最新支持情况,请访问https://www.oracle.com/technetwork/java/java-se-supportroadmap.html. ...

  3. Java8实战 阅读二周目感想

    Java8实战是我目前看过的写的水平最高的一本书,由浅入深,深入浅出,九浅一深. 之前大略的过了一遍,但是对于前几章的内容一直有点雾里看花的感觉. 又读了一遍,感觉有点新的感想. 一.其中1.2.1中 ...

  4. 《Java8实战》读书笔记10:组合式异步编程 CompletableFuture

    <Java8实战>读书笔记10:组合式异步编程 CompletableFuture 第11章 CompletableFuture:组合式异步编程 11.1 Future 接口 (只是个引子 ...

  5. Java8实战学习笔记(三)——函数式数据处理

    一.引入流 (一).引言 1.流是什么 流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现).可以看成遍历数据集的高级迭代器. 流可以透明地并行 ...

  6. [201604]Java8实战(陆明刚 劳佳 译)

    ==[201604]Java8实战(陆明刚 劳佳 译)== 第一部分 基础知识 第 1 章 为什么要关心 Java 8 1.1 Java 怎么还在变 1.1.1 Java 在编程语言生态系统中的位置 ...

  7. Java8实战学习笔记(四)——高效 Java 8 编程(一)

    一.重构.测试和调试 (一).为改善可读性和灵活性重构代码 用更紧凑的方式描述程序的行为 -- Lambda表达式 将一个既有的方法作为参数传递给另一个方法 -- 方法引用 如何运用前几章介绍的Lam ...

  8. Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序

    Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序 C#原本是用来编写Windows以及Windows Phone的应用程序.自从Xamarin问世后,C#的作用就发生了很大的变化. ...

  9. 《Java8实战》笔记汇总

    <Java8实战>笔记(01):为什么要关心Java8 <Java8实战>笔记(02):通过行为参数传递代码 <Java8实战>笔记(03):Lambda表达式 & ...

最新文章

  1. TOJ4537: n阶行列式
  2. Codeforces#371 Div2
  3. 《Effective Java》第8章 通用程序设计
  4. BringWindowToTop(), SetForegroundWindow(), SetActiveWindow()
  5. Perl函数pack/unpack(二进制读写)
  6. python快速上手下载_初学者如何尽快上手python
  7. html桌面雪花,html5 canvas雪花形状在线生成器
  8. 2011年数据库大会纪行
  9. WPF 依赖属性详解【转】
  10. 缓存面试 - 为什么要用缓存?缓存使用不当会造成什么后果?
  11. Jmeter简单的登录压力测试(使用json发送post请求)
  12. [漏洞案例]thinkcmf 2.x从sql注入到getshell实战
  13. android 动画基础,Android 动画基础
  14. GOOGLE本地搜索
  15. 我是如何完美解决WIN10崩溃无法自动恢复启动问题的
  16. Foxit PDF SDK for Linux (C++ Library) 8.4.1 Crack
  17. 一个事物两个方面的对比举例_写compare contrast essay如何对比两个事物/人物
  18. 【js】js获取今日和昨日0点和23点59分59秒
  19. 阿正入门深度学习---从EM算法开始
  20. Solidworks如何打开swb文件

热门文章

  1. Lazada代运营分享—Lazada新手运营快速提升流量交易额的三大核心技巧
  2. 离散化:两种离散化方式详解
  3. 程序员掌握linux命令,程序员必须知道的linux命令
  4. HTML:网页设计案例4
  5. assertThat使用方法
  6. Word中批量更新域的两个小方法
  7. AMD EPYC 7763/7T83/7713/7H12/7742 双路 8卡GPU服务器aleo
  8. Android中收货地址管理Demo
  9. 基于Sequoia DB巨杉数据库的投资组合评比器(设计思路)
  10. Spring - Spring配置文件-Spring配置数据源详解