Map.clear() vs new Map:哪一个更好?

111

我有一个Map变量,其语法为Map<String, String> testMap = new HashMap<String, String>();。 这个map里可能会有1000个数据。

当我的应用程序需要新的数据列表时,我必须清空Map。但是当我看到Map.clear()的代码时:

/**
     * Removes all of the mappings from this map.
     * The map will be empty after this call returns.
     */
    public void clear() {
        modCount++;
        Entry[] tab = table;
        for (int i = 0; i < tab.length; i++)
            tab[i] = null;
        size = 0;
    }

我意识到清除方法会循环n次(其中n是Map中数据的数量)。因此,我认为可以通过重新定义Map为testMap = new HashMap<String,String>(); 并且以前使用的Map将被垃圾回收。

但我不确定这是否是一个好方法。我正在开发移动应用程序。

你能给我指点吗?


3
很久以前发布了这个问题,但是无论如何,Google仍然引导我们来到这里。因此,使用clear而不是new的另一个优点就是您可以将地图声明为final,这样编译器就能够免费检测编程错误。对于其他类型的结构,“final”也会消除分配新内存的负担,使我们的代码(更)快。 - earizon
现在已经更久远了!但我还是找到了。我从并发的角度来看待这个问题。我的单例类是多线程的,我希望确保当我“清空”Map时不会花费太长时间并导致并发修改异常。所以我认为对我来说最好的方法是定义一个新的Map,如果另一个线程正在访问该Map,它将使用旧的引用,下一次则会使用新的空Map。 - Aaron
7个回答

124

这是一个比较复杂的问题,让我们看看会发生什么。

您实例化了一个新实例,它被新数组支持。因此,垃圾回收器应该清除先前映射中的所有键和值,并清除对自身的引用。因此,无论如何都会执行O(n)算法,但在垃圾回收器线程中进行。对于1000条记录,您不会看到任何差异。

但是,性能指南告诉您如果可以的话最好不要创建新对象。因此,我会选择使用clear()方法。

无论如何,请尝试两种变体并尝试测量。始终进行度量!


2
在阅读指南后,我意识到自己之前的困惑所在。谢谢。 - Pankaj Kumar
2
请看我对@Tanveer答案的评论。你无法估计GC的成本。 - Jason

33

当你在一个大小为 n 的 Map 上调用 Map.clear() 时... 你正在要求垃圾回收器清理 2*n(键和值)个对象。而当你对同一个 Map 调用 null 时,你正在要求垃圾回收器清理 2*n+1 个对象(其中一个是 Map 本身)。然后你将不得不创建一个新的 Map 实例,这又会带来额外的开销。因此建议使用 Map.clear()。在实例化 Map 时,设置 Map 的大小是明智的选择。


3
如果Map中的键和值在应用程序的其他地方被引用,那么您提供的大O估计仅适用于没有任何引用的键和值。如果有任何键和值在应用程序的其他地方被引用,则它们将不会被垃圾回收。也许我误解了,如果“清理”是指垃圾收集器必须检查对象以查看是否需要进行垃圾回收,则存在另一个问题...即使没有对它们的引用,垃圾收集器也可能永远不会清理这些对象:https://dev59.com/anE95IYBdhLWcg3wAo9U#2506525。我的意思是,当更改映射引用时,您无法估计GC的成本。 - Jason
我不相信事情会这样发展。GC不会立即采取行动,只有在工作负荷较轻或进行内存重组时才会这样做。 - navderm

12

我认为在Java中创建对象在内存方面更加昂贵,所以最好使用.clear(),这样你可以重复使用同一个对象而不是创建新的对象。


6
清除(clear)方法的概念是从映射中删除对其他对象的引用,以便在“映射在其他地方被引用”的情况下,键/值不会被gc卡住。
但是,如果您的映射只是由您特定的代码使用的本地映射(即“映射未在其他地方引用”),那么请使用新映射,但设置1000个引用为null也不会对性能造成大的影响。

1
对于1000个引用,这不会对性能造成很大的影响。我想知道创建一个新实例是否比使用clear方法更好。并且使用新实例是否有任何缺点? - Pankaj Kumar
1
主要缺点是,使用new时,旧的HashMap仍然在内存中,直到垃圾收集器释放它。这将增加一些内存开销和更多的处理开销,以便GC释放它。无论哪种情况,重用或创建新选项的差异都将是微不足道的(除非您的应用程序只是创建HashMaps)。 - SJuan76

4

不要忘记地图的重新填充

如果在新的地图中没有指定容量,由于重新哈希(每个都是O(n)且发生O(log(n))次),你会得到相当多的开销,尽管这可能平均总共为O(n),但如果它们一开始就不发生,你仍然会更好一些.

在清除后的地图中不会发生这种情况,因为容量不会改变。


2

我认为调用new HashMap()是一个更好的选择,因为它不需要像清除hashmap那样进行大量处理。此外,通过创建新的hashmap,您可以消除hashmap仍可能绑定到使用数据的控件的机会,这将在清除hashmap时引起问题。


1

调用 map.clear() 会清除所有数据。请注意,这只会丢弃所有条目,但会保持内部用于存储条目的数组大小相同(而不是缩小到初始容量)。如果您还需要消除它,最简单的方法是丢弃整个 HashMap 并将其替换为新实例。当然,这仅适用于您控制谁拥有指向 Map 的指针。

至于回收内存,您必须让垃圾收集器完成其工作。

您的值也是 Long 型吗?在这种情况下,您可能希望查看比通用 HashMap 更(内存)高效的实现,例如在 GNU Trove library 中找到的 TLongLongHashMap。那应该可以节省很多内存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接