【问题标题】:Using ConcurrentHashMap efficiently?有效地使用 ConcurrentHashMap?
【发布时间】:2013-09-01 05:19:46
【问题描述】:

我有一个 Android 应用程序,其核心组件是 HashMap<String,float[]>. 系统具有高并发性。例如 以下是我经常发生的以下三种情况,它们本质上是高度重叠的

  1. 遍历 hashmap 中的所有键并对 它的值(只读操作)。
  2. 在 Hashmap 中添加新的键值对。
  3. 从 Hashmap 中删除某些键。

我在不同的线程中执行所有这些操作,因此我使用 ConcurrentHashMap 因为检索中的一些不一致并不重要。例如在迭代地图时,如果添加了新条目,那么不立即读取这些新值并不重要,因为我确保下次读取它们。

此外,在删除条目时,我每次都重新创建迭代器以避免“ConcurrentModificationException”

假设,有以下hashmap(即ConcurrentHashmap)

ConcurrentHashMap<String,float[]> test=new ConcurrentHashMap<String, float[]>(200);

现在为了检索,我执行以下操作

Iterator<String> reader=test.keySet().iterator();
            while(reader.hasNext())
            {
                String s=reader.next();
                float[] temp=test.get(s);
                //do some operation with float[] temp here(read only operation)
            }

对于删除,我执行以下操作

boolean temp = true;
        while (temp) {
            for (String key : test.keySet()) {
                temp = false;
                if (key.contains("abc")) {
                    test.remove(key);
                    temp = true;
                    break;
                }
            }
        }

当插入新值时,我只是这样做

test.put("temp value", new float[10]);

我不确定它是否是一种非常有效的利用方式。另外,不要读入已删除的值也很重要(但是我需要效率,并且由于在函数调用期间再次创建了迭代器,因此可以保证下次我不会得到已删除的值),因此可能会出现很多不一致能忍受吗?

有人可以告诉我一个有效的方法吗?

附言。我忘了提到为什么我要以这种方式进行删除操作。 我现在已经改变了删除它的条件,从等于包含(可能有多个stings具有前缀“abc”后跟不同的后缀。所以我需要删除所有这些。

【问题讨论】:

  • 迭代整个地图以删除一个条目当然效率不高。为什么不直接调用 remove("abc")?
  • 是的,哎呀,我现在已将删除条件更改为包含..
  • 不,您应该将整个块更改为仅对 remove() 的一次调用。你不需要其他任何东西,我不明白你为什么不这么认为。 –1

标签: java android concurrency concurrenthashmap


【解决方案1】:

遍历 hashmap 中的所有键并对其值进行一些操作(只读操作)。

不要遍历键集然后也检索值 - 直接遍历条目集:

for (Map.Entry<String, float[]> e : map.entrySet() {
    String key = e.getKey();
    float[] value = e.getValue();
    //do something with them
}

这通常更有效(即使对于“普通”HashMaps),但它也会减少您的情况下的争用(对地图的访问次数减半)。

在 Hashmap 中添加新的键值对。

是的,很简单:map.put(s, f);

从 Hashmap 中删除某些键。

如果您需要检查键是否包含给定的子字符串,那么您确实需要像您正在做的那样迭代键,尽管我不确定为什么您使用 while+for+break 而不是简单的 for。

【讨论】:

  • 好吧..所以迭代入口集总是更快??即使是删除操作,我也要这样做吗??
  • @DebarshiDutta 不,如果您需要同时访问键和值,它会更快 - 如果您只关心键(例如在删除中),则迭代 keySet 会更有效。
【解决方案2】:

由于您使用ConcurrentHashMap 的方式,您正在删除它的Concurrent 特征。您的(重新)同步尝试会非常频繁,但并非总是如此。

您是否考虑过将keys 留在HashMap 中?我正在考虑类似的事情:

    public static final float[] DELETED= new float[0] ;

    /* delete */
    test.put(key,DELETED);

    /* insert */
    test.put(key,value);

    /* iterate */
    for(Map.Entry<String,float[]> e: test.entrySet ) {
        if( e.getValue() != DELETED ) {
            operateOn(e);
        }
    }

如果密钥太不稳定(即一段时间后您将有太多已删除的项目),那么您可以创建清理 Thread

【讨论】:

    【解决方案3】:

    根据 ConcurrentHashMap API,它的迭代器永远不会抛出 ConcurrentModificationException,因此您不需要在删除后中断。但无论如何,迭代和删除的正确方法是这样的

    for (Iterator<String> i = test.keySet().iterator(); i.hasNext();) {
         String next = i.next();
         if (next.equals("abc")) {
                 i.remove();
         }
    }
    

    这样即使在没有 ConcurrentModificationException 的快速失败迭代器中也能正常工作

    【讨论】:

    • 好吧..我不看它..我会改变它
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多