【问题标题】:Is it possible to iterate a ConcurrentHashMap without creating new objects?是否可以在不创建新对象的情况下迭代 ConcurrentHashMap?
【发布时间】:2021-03-23 01:41:09
【问题描述】:

在分析我的 android 游戏后,我注意到在我在整个主游戏循环中调用的简单迭代过程中生成的 ConcurrentHashmap 数量异常。代码如下

    public void checkIfStillNeedsToShowUI() {

      for (Map.Entry<String, GameUI> gameUIEntry : listOfUIObjects.entrySet()) {
        if(!gameUIEntry.getValue().isShowing()){//ignore what not showing
            continue;
        }
        final GameUI tmpGameUI = (gameUIEntry.getValue());
        if(!tmpGameUI.hasReasonForShowing()){
            continue;
        }

        if(tmpGameUI.reasonForShowing.checkReason()){
            tmpGameUI.setShowing(true);
        } else {
            tmpGameUI.setShowing(false);
        }

    }
}

结果如下

这正常吗?还是我做错了什么?我知道使用通用/增强的 for 循环类型会导致创建一个对象以便访问它,但我目前不知道另一种迭代哈希图的方法,它会给我想要的结果。

【问题讨论】:

  • 使用Map.forEach 代替for (entry : entrySet()) 可能会减少分配的数量,但您可能无法将其归零。
  • 有趣的是,对象被命名为listOfUIObjects,但实际上是Map
  • @LouisWasserman 试一试。可悲的是,我不得不将我的最小 API 提高到 24。
  • 答案是,不,你没有做错任何事,但不可能在不分配对象的情况下迭代 ConcurrentHashMap。遗憾的是,JDK api 确实经常分配对象。在我的工作场所,我们编写零分配 java,但我们通过创建自己的地图实现来实现,该实现具有可重用的迭代器实例,可以在使用前重置。如果您完全有可能只使用一个线程来读/写,那么编写自己的不分配的单线程映射实现会容易得多。祝你好运。
  • 查看这个库,了解出色的单线程快速非分配集合实现,包括映射:github.com/real-logic/agrona。对象到对象映射实现在这里:github.com/real-logic/agrona/blob/master/agrona/src/main/java/….

标签: java android loops optimization hashmap


【解决方案1】:

它们不是ConcurrentHashMap 的实例,它们是MapEntry 的实例。

如果您的意思是 MapEntry 实例,答案是没有 JDK 在 ConcurrentHashMap 迭代期间创建这些对象的新实例,当您使用 ConcurrentHashMap 时这是不可避免的,您可以在 next 方法中看到EntityIterator ConcurrentHashMap 里面的类。问题在于,要管理并发 JDK 存储类型为 Node 的对象,并且这些对象不被视为已导出,如源代码中的文档中所述:

键值输入。此类永远不会作为用户可变的 Map.Entry 导出(即,一个支持 setValue;请参阅下面的 MapEntry),但可用于批量任务中使用的只读遍历。具有负哈希字段的 Node 子类是特殊的,并且包含空键和值(但从不导出)。否则,keys 和 vals 永远不会为空。

因此,ConcurrentHashMap 中的 EntryIterator 类将这些对象转换为 next 方法中的 MapEntry。如果您只在单线程应用程序中使用地图,则可以改用HashMap

【讨论】:

  • 使用 HashMap 无济于事。这也会在迭代时分配对象。一般来说,JDK 集合通常会分配,这就是我个人不使用它们的原因。
  • @junkie 你能提到在HashMap 源代码中,当HashMap 被迭代时,这些对象是在哪里分配的?
  • entrySet() 创建一个 EntrySet 并且 EntrySet 在迭代时创建一个新的 EntryIterator。我想我看到了你所指的区别。我说的是一般的分配。即使它没有在每次迭代中分配,它分配的事实仍然是分配。我上面链接的集合根本不分配。任何分配都可能导致堆填满,具体取决于调用频率。虽然很可能 OP 正在尝试减少分配,但可能不会完全消除它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-05
  • 1970-01-01
  • 2011-08-19
  • 1970-01-01
  • 1970-01-01
  • 2010-12-22
  • 2018-10-31
相关资源
最近更新 更多