【问题标题】:Iterate over an EnumMap which doesn't lead to a new object creation per iteration迭代 EnumMap 不会导致每次迭代创建新对象
【发布时间】:2017-01-17 17:20:12
【问题描述】:

有没有一种方法可以遍历 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;
    }

【问题讨论】:

  • 您自己的解决方案有什么不好?
  • for (V value : map.values()) foo(value); 怎么样?除了检索值之外,您似乎没有将键用于任何其他用途。
  • @VGR 公平点。我也需要钥匙。
  • 在这种情况下,你就不走运了。您可以编写自己的 EnumMap 实现来执行此操作,但如果您需要返回键和值的东西,但不创建新对象,您的解决方案绝对不会是惯用的。
  • @RealSkeptic 是的,我目前的方法还不错。我很惊讶地发现 EnumMap 实现即使在 Java 8 中也没有很好的 foreach 覆盖。只是在这里确认我的理解并检查是否有更好的方法。

标签: java keyset enum-map


【解决方案1】:

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

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

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

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

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 是的,对象创建是否是一个问题不是问题。我正在寻找一种惯用的方法来做到这一点,而不必求助于保留枚举数组。
    • 为什么要保留枚举数组?
    • @marstran 因为没有一种形式的 EnumMap#contains(...) 采用常量的 int 序数。
    【解决方案3】:

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

    是的,有更好的方法:

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

    或 Java 8 版本:

    map.forEach((k, v) -> {...});
    

    【讨论】:

    • 我在这里非常具体地谈论 EnumMap。 entrySet 迭代器如下所示 public Map.Entry next() { if (!hasNext()) throw new NoSuchElementException(); lastReturnedEntry = new Entry(index++);返回最后一个返回条目; }
    猜你喜欢
    • 1970-01-01
    • 2018-06-21
    • 1970-01-01
    • 2014-06-30
    • 2021-08-31
    • 1970-01-01
    • 2019-01-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多