使用巨大的地图(putIfAbsent)

时间:2013-11-08 11:45:54

标签: java performance map

我有这个地图定义:

TreeMap <String, Set<Integer>>

它可能包含数百万个条目,我还需要一个“自然顺序”(这就是为什么我选择了一个TreeMap,但如果需要我可以编写一个Comparator)。

所以,为了向地图添加元素我需要做的是:

  1. 检查密钥是否已存在。
  2. 如果没有,请创建一个新的Set并添加值。
  3. 如果存在,我必须将值添加到Set
  4. 我有这个实现工作正常:

    private void addToMap (String key, Integer value){
        Set<Integer> vs = dataMap.get(key);
        if (vs == null){
            vs = new TreeSet<Integer>();
            dataMap.put(key,vs);
        }
        vs.add(value);
    }
    

    但我想避免搜索密钥然后放置元素(如果它不存在)(它将在巨大的地图上执行新的搜索)。

    我想我可以使用ConcurrentHashMap.putIfAbsent方法,但是:

    1. 我没有按键的自然顺序(我需要对数以百万计的密钥进行排序)
    2. 由于ConcurrentHashMap的同步,我可能有(我不知道)额外的开销,在我的情况下,我的进程是单线程的,可能会影响性能。
    3. 阅读这篇文章:Java map.get(key) - automatically do put(key) and return if key doesn't exist? 有一个关于Guava MapMaker.makeComputingMap的答案,但看起来这个方法已经不存在了。

      在这种情况下,性能至关重要(一如既往:D),所以请告诉我你的建议。

      提前致谢。

      注意: 非常感谢您在短短几分钟内获得了许多帮助答案。 (我不知道选哪一个是最好的。)

      我将对建议(TreeMultiMap,ConcurrentSkipListMap,TreeSet + HashMap)进行一些性能测试,并使用结果进行更新。我将选择性能最佳的那个,因为我想选择所有三个,但我不能。

      注2

      所以,我做了一些150万条目的性能测试,结果如下:

      • ConcurrentSkipListMap,它不能按预期工作,因为它用我提供的新空集替换现有值。我认为只有当密钥不存在时才设置值,所以我不能使用这个。 (我的错误)。

      • TreeSet + HashMap,工作正常,但没有提供最佳性能。它比单独的TreeMap或TreeMultiMap慢1.5倍。

      • TreeMultiMap提供了最佳性能,但它几乎与TreeMap相同。我将检查这一个作为答案。

      再次感谢您的贡献和帮助。

3 个答案:

答案 0 :(得分:2)

如果性能至关重要,我不会使用整数TreeSet,我会找到一个更轻量级的结构,如TIntArrayList或包含int值的东西。我也会使用HashMap,因为它的查找是O(1)而不是O(log N)。如果您还需要对键进行排序,我会使用第二个集合。

我同意ConcurrentHashMap上的putIfAbsent是矫枉过正的,并且获取/放置在HashMap上可能是最快的选择。

ConcurrentSkipListMap可能是使用putIfAbsent的一个很好的选择,但我会确保它不会慢。

BTW甚至比执行get / put更糟糕的是创建一个你不需要的HashSet。

答案 1 :(得分:2)

PutIfAbsent具有并发的好处,即:如果许多线程同时调用它,则它们不必等待(它不在内部使用synchronized)。然而,这只是执行速度的一小部分,所以如果你只使用单线程,这将减慢速度。

如果您需要将其排序,请尝试ConcurrentSkipListMap

答案 2 :(得分:2)

  • 并发映射不会产生魔法,它会检查存在,然后插入(如果不存在)。
  • Guava有MultiMaps,例如TreeMultiMap可以是你需要的。