1

有没有一种方法可以遍历 EnumMap 而不会导致每次迭代都创建新的对象?条目集的迭代器每次都返回一个新条目。我能看到的唯一方法是

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;
    }
4

3 回答 3

2

首先,从您所说的看来,您似乎希望迭代器返回两个对象的元组。

在 Java 中,这样做的唯一方法是将它们包装在另一个对象中。(在撰写本文时,就是这样。)所以迭代器必须返回一个对象,而不是键和值。该对象必须在调用next()返回之前的某个时间点创建。

考虑到这一限制,可以采取三种可能的途径:

  1. 在 上创建此条目对象put()
  2. 在第一次迭代时创建条目对象 entrySet()(但之后将其缓存)。
  3. 在 的每次迭代上创建一个新的条目对象entrySet()

内置EnumMap选项 3 可能的原因是它实现起来最简单,并且如果您不需要遍历条目,它是最经济的解决方案。缺点是如果您需要多次迭代,您创建的对象比任何其他解决方案都多。

选项 1 实现起来同样简单,但每次向地图添加条目时都会产生明显的开销,即使您从未打算访问它也是如此。

最后,选项 2 涉及稍微更多的代码复杂性,以及在迭代和添加更多元素之间交替时的几个极端情况,但它在理论上为您提供了最佳的内存配置文件。

如果多次迭代的内存开销被证明是您的应用程序中的一个问题,您可以轻松实现选项 2,但我怀疑在大多数情况下差异会很明显。

Ps:如果您愿意偏离惯用的解决方案并进入稍微疯狂的领域,您可以Map.Entry为所有条目重复使用相同的实例。这显然与我们对 a 的期望相矛盾Map.Entry,但它为您提供了最小的内存分配开销,并且您可以在简单的迭代场景中摆脱它。任何人都在猜测您是否最终得到更快的最终产品,您需要对其进行衡量。

于 2017-01-17T19:27:58.743 回答
1

我反思性地怀疑这种对对象创建的关注程度的合法性。但是,如果避免创建对象真的很重要,那么您可以维护自己的枚举常量数组并map.contains(...)为每个常量进行测试。您必须对此进行测试以查看性能比较。

于 2017-01-17T17:28:17.863 回答
0

首先,您的代码不会每次都创建一个对象。它只获取对现有对象的引用。

是的,有一个更好的方法:

for (Map.Entry<K, V> entry : map.entrySet()) {
    // use entry.getKey() and entry.getValue()
}

或 Java 8 版本:

map.forEach((k, v) -> {...});
于 2017-01-17T17:24:56.100 回答