迭代 EnumMap 不会导致每次迭代都创建新对象
Iterate over an EnumMap which doesn't lead to a new object creation per iteration
有没有一种方法可以迭代 EnumMap 而不会导致每次迭代都创建新对象? entryset 的迭代器 returns 每次都是一个新的 Entry。我能看到的唯一方法是
for(K k: map.keySet())
foo(k, map.get(k));
澄清这是关于 EnumMap 的,它在其 EntrySet 上有以下迭代器实现
public Map.Entry<K,V> next() {
if (!hasNext())
throw new NoSuchElementException();
lastReturnedEntry = new Entry(index++);
return lastReturnedEntry;
}
首先,您的代码并不是每次都创建一个对象。它仅获得对现有对象的 引用。
是的,有更好的方法:
for (Map.Entry<K, V> entry : map.entrySet()) {
// use entry.getKey() and entry.getValue()
}
或Java8版本:
map.forEach((k, v) -> {...});
我本能地怀疑这种对对象创建的关注的合法性。但是,如果避免对象创建真的如此重要,您可以维护自己的枚举常量数组并为每个数组测试 map.contains(...)
。您必须对此进行测试以查看性能比较。
首先,从您所说的看来,您希望迭代器 return 两个对象的元组。
在 Java 中,唯一的方法是将它们包装在另一个对象中。 (在撰写本文时,就是这样。)所以迭代器必须 return 除了键和值之外的对象。该对象必须在调用 next()
returns.
之前的某个时刻创建
考虑到这一限制,可以采取三种合理的途径:
- 在
put()
上创建此条目对象。
- 在
entrySet()
的第一次迭代中创建条目对象(但之后缓存它)。
- 在
entrySet()
的每次迭代中创建一个新的条目对象。
built-in EnumMap
选择选项 3 的可能原因是它最容易实现,而且如果您不需要遍历条目,它是最经济的解决方案。缺点是如果你需要多次迭代,你创建的对象比任何其他解决方案都多。
选项 1 实施起来同样简单,但每次向地图添加条目时都会产生明显的开销,即使您从未打算访问它也是如此。
最后,选项 2 涉及稍微复杂的代码,以及在迭代和添加更多元素之间交替时的一些边缘情况,但它为您提供理论上最好的内存配置文件。
如果多次迭代的内存开销被证明是您的应用程序中的一个问题,您可以轻松地实施选项 2,但我怀疑在大多数情况下差异是否会很明显。
P.s.: 如果您愿意偏离惯用的解决方案并进入稍微疯狂的领域,您可以 re-use 所有条目的相同 Map.Entry
实例。这显然与我们对 Map.Entry
的期望相矛盾,但它为您提供了最小的内存分配开销,并且您可以在简单的迭代场景中摆脱它。是否最终得到更快的最终产品是任何人的猜测,您需要对其进行衡量。
有没有一种方法可以迭代 EnumMap 而不会导致每次迭代都创建新对象? entryset 的迭代器 returns 每次都是一个新的 Entry。我能看到的唯一方法是
for(K k: map.keySet())
foo(k, map.get(k));
澄清这是关于 EnumMap 的,它在其 EntrySet 上有以下迭代器实现
public Map.Entry<K,V> next() {
if (!hasNext())
throw new NoSuchElementException();
lastReturnedEntry = new Entry(index++);
return lastReturnedEntry;
}
首先,您的代码并不是每次都创建一个对象。它仅获得对现有对象的 引用。
是的,有更好的方法:
for (Map.Entry<K, V> entry : map.entrySet()) {
// use entry.getKey() and entry.getValue()
}
或Java8版本:
map.forEach((k, v) -> {...});
我本能地怀疑这种对对象创建的关注的合法性。但是,如果避免对象创建真的如此重要,您可以维护自己的枚举常量数组并为每个数组测试 map.contains(...)
。您必须对此进行测试以查看性能比较。
首先,从您所说的看来,您希望迭代器 return 两个对象的元组。
在 Java 中,唯一的方法是将它们包装在另一个对象中。 (在撰写本文时,就是这样。)所以迭代器必须 return 除了键和值之外的对象。该对象必须在调用 next()
returns.
考虑到这一限制,可以采取三种合理的途径:
- 在
put()
上创建此条目对象。 - 在
entrySet()
的第一次迭代中创建条目对象(但之后缓存它)。 - 在
entrySet()
的每次迭代中创建一个新的条目对象。
built-in EnumMap
选择选项 3 的可能原因是它最容易实现,而且如果您不需要遍历条目,它是最经济的解决方案。缺点是如果你需要多次迭代,你创建的对象比任何其他解决方案都多。
选项 1 实施起来同样简单,但每次向地图添加条目时都会产生明显的开销,即使您从未打算访问它也是如此。
最后,选项 2 涉及稍微复杂的代码,以及在迭代和添加更多元素之间交替时的一些边缘情况,但它为您提供理论上最好的内存配置文件。
如果多次迭代的内存开销被证明是您的应用程序中的一个问题,您可以轻松地实施选项 2,但我怀疑在大多数情况下差异是否会很明显。
P.s.: 如果您愿意偏离惯用的解决方案并进入稍微疯狂的领域,您可以 re-use 所有条目的相同 Map.Entry
实例。这显然与我们对 Map.Entry
的期望相矛盾,但它为您提供了最小的内存分配开销,并且您可以在简单的迭代场景中摆脱它。是否最终得到更快的最终产品是任何人的猜测,您需要对其进行衡量。