4

我试图在 Java 中迭代 HashMap 的值,而不创建任何需要进行垃圾收集的新对象。使用增强的 for 循环很容易迭代值,如下所示:

for (Value v : myMap.values()) {
    ....
}

但这将在幕后创建一个 Iterator 对象(我认为?)

我想出的最好的是这段代码:

Object[] values = myMap.values.toArray();
...standard "int i" for loop on the array

但老实说,我不确定 GC 会对这个数组做什么。

有没有一种完美的方法来做到这一点,或者有没有办法用一个可重用的对象来做到这一点?

编辑:这是针对 Android 游戏的,有很多这样的循环会创建对象并影响性能。

编辑#2:对于那些怀疑的人,我重构了最常被称为增强 for 循环的一部分,主要是在 ArrayLists 上,对基于 jvisualvm 堆分析的内存使用产生了重大影响。当然,部分原因可能是因为我有太多的 ArrayList,或者在没有必要时循环它们。

4

2 回答 2

3

但这将在幕后创建一个 Iterator 对象(我认为?)

是的,它会的。

我想出的最好的是这段代码:

  Object[] values = myMap.values.toArray();
  ...standard "int i" for loop on the array

但老实说,我不确定 GC 会对这个数组做什么。

事实上,这比Iterator显式或隐式使用更糟糕:

  • toArray()方法分配一个新数组并将值集元素复制到其中。
  • 调用values() 可以实例化一个Set对象。
  • 调用toArray()内部调用iterator()创建新Iterator实例的值集对象。

所以你分配了一个Iterator无论如何,一个临时数组,和(可能)一个Set对象。


我分析了堆,最大的对象集是ArrayList$Itr(与此HashMap示例无关,但我目前正在将所有增强的 for 循环重写为标准 for 循环)

正如你所指出的,这是一个不同的情况。但我仍然认为你在这里吠错了树。如果您使用的是最近的 HotSpot JVM,那么分配和垃圾收集短期对象(即那些没有获得终身使用权的对象)的成本非常小。除非您有特定的理由来降低对象分配率,否则您的所有工作在实际可衡量的性能改进方面可能收效甚微。

于 2013-08-21T08:55:59.720 回答
2

到目前为止,for-each 循环是您最好的选择:它高效且易读。

老实说,您会注意到在尝试避免创建迭代器方面没有任何变化。迭代器是专门为迭代而设计的,因此这样做是高效且优化的。创建数组是您最糟糕的选择:它为已经在完美可迭代数据结构中的对象分配内存。

关于您的编辑:您确定是因为迭代器吗?你真的做了微基准测试吗?我很确定在这个循环中还有其他东西比简单地创建一个非常轻的对象需要更多的时间在使代码复杂化之前,尝试弄清楚那是什么并优化真正重要的东西。

于 2013-08-21T07:34:40.410 回答