【问题标题】:Most efficient way to clear a Java HashMap [duplicate]清除Java HashMap的最有效方法[重复]
【发布时间】:2014-02-22 15:11:18
【问题描述】:

使用 Java,我有一个包含一些项目的 Map 接口。我想清除其中的所有数据以再次使用它。哪种方法更有效?

params.clear() 

params = new HashMap();

【问题讨论】:

  • clear() 将重用更多对象(至少 1 个,具体取决于实现)。 clear() 的运行时和运行 HashMap 的构造函数的比较取决于实现。我猜clear() 跑得更快。
  • 也许您应该编写一个测试并评估性能?

标签: java


【解决方案1】:

我更喜欢clear(),因为您可以将Map 设为final 成员。

class Foo {
    private final Map<String, String> map = new HashMap<String, String>();

    void add(String string) {
        map.put(string, "a value");
    }

    void clear() {
        map.clear();
    }
}

如果您每次都分配一个新的Map,您可能会遇到多线程问题。


下面是一个使用 Map 包裹在 Collections.synchronizedMap 中的几乎线程安全的示例,但它会在您每次清除它时分配一个新映射。

class MapPrinter {

    private static Map<String, String> createNewMap() {
        return Collections.synchronizedMap(new HashMap<String, String>());
    }

    private Map<String, String> map = createNewMap();

    void add(String key, String value) {
        // put is atomic due to synchronizedMap
        map.put(key, value);
    }

    void printKeys() {
        // to iterate, we need to synchronize on the map
        synchronized (map) {
            for (String key : map.values()) {
                System.out.println("Key:" + key);
            }
        }
    }

    void clear() {
        // hmmm.. this does not look right
        synchronized(map) {
            map = createNewMap();
        }
    }
}

clear 方法导致了一个大问题:synchonized(map) 将不再按预期工作,因为 map 对象可以更改,现在两个线程可以同时位于这些 synchronized 块中,因为它们不会锁定同一个对象。为了实现真正的线程安全,我们要么必须完全在外部同步(.synchronizedMap 将毫无用处),要么我们可以简单地将其设为final 并使用Map.clear()

void clear() {
    // atomic via synchronizedMap
    map.clear();
}

final Map(或任何final)的其他优点

  • 没有额外的逻辑来检查null 或创建一个新的。更改地图可能需要编写的代码开销非常大。
  • 不会意外忘记分配Map
  • “Effective Java #13: Favor Immutability” - 虽然地图是可变的,但我们的参考不是。

【讨论】:

  • 您在谈论什么样的问题?在这方面,我看不出这比创建一个新的Map 更好。
  • @zapl :您已将地图定义为最终地图,因此您只需清除它。而且,这些多线程问题是什么?
  • 保证任何线程都能看到final 变量。非最终的一切都可能需要volatile 或其他同步方法。如果您创建新地图,synchronized(map) 也会失败,因为线程可以在不同的对象上同步。
  • @zapl 虽然这是一个很好的论点,但您提出的示例也不是线程安全的(例如,map.clear() 不是原子的,如果同时调用add,实际上可能会引发异常)。 ..这并不能真正回答“哪个更有效”的问题。
  • @zapl 我的意思是 HashMap 无论如何都不是线程安全的,所以无论你清除它还是使用new HashMap 从线程安全的角度来看都没有区别:你必须在这两种情况下都添加同步。
【解决方案2】:

一般来说: 如果您不知道clear() 是如何实现的,那么您无法猜测哪个会更高效。我可以提出一个或另一个肯定会获胜的综合用例。 如果您的地图不包含数百万或数百万条记录,您可以选择任何一种方式。性能是一样的。

具体来说: HashMap 通过擦除内部数组的内容来清除。立即为 GC 提供旧地图内容。当您创建一个新的 Hashmap 时,它还会使旧地图内容可用于 GC + HashMap 对象本身。您正在用几个 CPU 周期来换取稍微少一点的 GC 内存

您需要考虑其他问题:

  • 您是否将此引用传递给其他代码/组件?您可能想使用clear() 以便其他代码看到您的更改,反向也是如此
  • 您想要没有麻烦、没有副作用的新地图吗?我会创建一个新的。

【讨论】:

    猜你喜欢
    • 2017-10-18
    • 1970-01-01
    • 1970-01-01
    • 2018-10-21
    • 1970-01-01
    • 1970-01-01
    • 2014-02-13
    • 1970-01-01
    • 2015-11-08
    相关资源
    最近更新 更多