Java Collections(集合) Bug

本贴最后更新于 2326 天前,其中的信息可能已经事过境迁

Java Collections(集合) Bug


maxresdefaultjpg


1.尝试在遍历中修改集合元素

假设现在公司经营不善,我们要解雇 32 岁的工程师 David。

List<String> engineers = new ArrayList<>(asList("Tom", "David")); System.out.println(engineers); for (String engineer : engineers) { if ("David".equalsIgnoreCase(engineer)) { engineers.remove(engineer); } } System.out.println(engineers);

大家都知道上面的代码会运行出错(java.util.ConcurrentModificationException),出现了两个 Object(Iterator 和 Collections) 我们可以使用 Iterator 来解决

List<String> engineers = new ArrayList<>(asList("Tom", "David")); System.out.println(engineers); for (Iterator<String> iterator = engineers.iterator(); iterator.hasNext();) { String next = iterator.next(); if ("David".equalsIgnoreCase(next)) { iterator.remove(); } } System.out.println(engineers); }

但是 8012 年了,这种方法是不是有一点 old school 呢?我们有更时尚一点的做法!在 Java 8 中引入了 Lambda 表达式,我们只需要一行代码即可。

engineers.removeIf(engineer -> "David".equalsIgnoreCase(engineer));

2.集合元素并发修改

假设公司经营不善,凡是受到 1000 次投诉以上的工程师要被解雇

private static final String NAME = "David"; private static final int TIMES = 1000; private static final ExecutorService executorService = Executors.newFixedThreadPool(2); public static void main(String[] args) { Map<String, BigDecimal> complaints = new HashMap<>(); //Map<String, BigDecimal> complaints = new ConcurrentHashMap<>() complaints.put(NAME, BigDecimal.ZERO); // seqAdd(complaints); concurrAdd(complaints); executorService.shutdown(); try { while (!executorService.awaitTermination(1, TimeUnit.SECONDS)) System.out.println(complaints); }catch (InterruptedException e) { e.printStackTrace(); } } private static void seqAdd(Map<String, BigDecimal> complaints) { for (int i = 1; i <= TIMES; i++) { addOneCompl(complaints); } } //并发投诉 private static void concurrAdd(Map<String, BigDecimal> complaints) { for (int i = 1; i <= TIMES; i++) { executorService.submit(() -> addOneCompl(complaints)); } } //投诉+1 private static void addOneCompl(Map<String, BigDecimal> complaints) { BigDecimal times = complaints.get(NAME); if (times != null) { complaints.put(NAME, times.add(BigDecimal.ONE)); } }

当我们运行 seqAdd(complaints)时一切正常,输出结果{David=1000},当我们换成 concurrAdd(complaints)时,输出结果{David=589},{David=601}达不到 1000。不是有一个 ConcurrentHashMap 的类吗?看名字似乎能解决我们的并发问题?真的吗?

Map<String, BigDecimal> complaints = new ConcurrentHashMap<>()

输出结果为{David=153},比前面的情况更糟糕!
我们可以在使用 synchronized 修饰 addOneCompl()方法,但这是一个坏方法。我们可以使用 Map.computeIfPresent()修改 addOneCompl()方法

private static synchronized void addOneCompl(Map<String, BigDecimal> complaints) { complaints.computeIfPresent(NAME, (name, times) -> times.add(BigDecimal.ONE)); }

这是一个在 java8 中存在的 api。源码是这样解释的

If the value for the specified key is present and non-null, attempts to * compute a new mapping given the key and its current mapped value
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3455 回帖 • 159 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3200 引用 • 8215 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...