具有良好性能的多图

时间:2010-08-10 04:39:03

标签: java performance multimap

在我的代码中,我有一张地图,在几秒钟内被大量使用了几千次。最初我有一个TreeMap,但在测试9,000个条目时,我看到我的旧处理器融化了。这需要扩大规模。所以我转移到了HashMap,性能非常出色。

现在我正在改变我的设计,正在寻找一个MultiMap。但是我害怕对get()方面的性能影响,因为它必须遍历所述大型地图,挑选匹配的密钥,并且当被调用很多次甚至同步时,它似乎会很慢。

是否有一个好的MultiMap可以处理如此大的值并具有出色的性能?性能在这个应用程序中至关重要,因为可能有许多大型单独的映射处理非常大的工作负载,使“小”性能损失成为非常大的问题。

如果可以提取单独工作而没有任何依赖性,则获得奖励积分。

5 个答案:

答案 0 :(得分:4)

在我的一个问题中向我推荐的是Apache Commons MultiMap: http://commons.apache.org/collections/api-3.2.1/org/apache/commons/collections/MultiHashMap.html

这是免费软件,因此您至少可以让源代码查看它,并根据您的许可证情况,您可以修改它或单独使用它。

它在内部使用了一个ArrayList,但我想你可能会改变它以使用HashSet或其他东西。我会看一下createCollection(Collection coll)方法。

更新:实际上,Guava的HashMultiMap似乎已经是我所说的: https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/Multimap.java

我查看了源代码,似乎每个值集合实际上都是由HashSet支持。

答案 1 :(得分:2)

我有一个要求,我必须有一个Map<Comparable, Set<Comparable>>,其中Map上的插入是并发的,也是相应的Set,但是一旦从Map中消耗了一个Key,就必须将其删除,如果作为一个每两秒运行一次的Job,它从一个特定的Key中消耗整个Set<Comparable>,但是插入完全是并发的,以便在Job启动时缓冲大多数值,这是我的实现:

注意:我使用Guava的帮助类Maps来创建并发映射,此解决方案还在实践清单5.19 中模拟 Java并发:

import com.google.common.collect.MapMaker;

import java.util.concurrent.ConcurrentMap;

/**
 * Created by IntelliJ IDEA.
 * User: gmedina
 * Date: 18-Sep-2012
 * Time: 09:17:50
 */
public class LockMap<K extends Comparable>
{
  private final ConcurrentMap<K, Object> locks;

  public LockMap()
  {
    this(16, 64);
  }

  public LockMap(final int concurrencyLevel)
  {
    this(concurrencyLevel, 64);
  }

  public LockMap(final int concurrencyLevel, final int initialCapacity)
  {
    locks=new MapMaker().concurrencyLevel(concurrencyLevel).initialCapacity(initialCapacity).weakValues().makeMap();
  }

  public Object getLock(final K key)
  {
    final Object object=new Object();
    Object lock=locks.putIfAbsent(key, object);
    return lock == null ? object : lock;
  }

}


import com.google.common.collect.MapMaker;
import com.google.common.collect.Sets;

import java.util.Collection;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;

/**
 * A general purpose Multimap implementation for delayed processing and concurrent insertion/deletes.
 *
 * @param <K> A comparable Key
 * @param <V> A comparable Value
 */
public class ConcurrentMultiMap<K extends Comparable, V extends Comparable>
{
  private final int initialCapacity;
  private final LockMap<K> locks;
  private final ConcurrentMap<K, Set<V>> cache;

  public ConcurrentMultiMap()
  {
    this(16, 64);
  }

  public ConcurrentMultiMap(final int concurrencyLevel)
  {
    this(concurrencyLevel, 64);
  }

  public ConcurrentMultiMap(final int concurrencyLevel, final int initialCapacity)
  {
    this.initialCapacity=initialCapacity;
    cache=new MapMaker().concurrencyLevel(concurrencyLevel).initialCapacity(initialCapacity).makeMap();
    locks=new LockMap<K>(concurrencyLevel, initialCapacity);
  }

  public void put(final K key, final V value)
  {
    synchronized(locks.getLock(key)){
      Set<V> set=cache.get(key);
      if(set == null){
        set=Sets.newHashSetWithExpectedSize(initialCapacity);
        cache.put(key, set);
      }
      set.add(value);
    }
  }

  public void putAll(final K key, final Collection<V> values)
  {
    synchronized(locks.getLock(key)){
      Set<V> set=cache.get(key);
      if(set == null){
        set=Sets.newHashSetWithExpectedSize(initialCapacity);
        cache.put(key, set);
      }
      set.addAll(values);
    }
  }

  public Set<V> remove(final K key)
  {
    synchronized(locks.getLock(key)){
      return cache.remove(key);
    }
  }

  public Set<K> getKeySet()
  {
    return cache.keySet();
  }

  public int size()
  {
    return cache.size();
  }

}

答案 2 :(得分:1)

选择很大程度上取决于你想做什么。有许多数据结构,有些在特定领域比其他更好,反之亦然。

我可以推荐你潜在的候选人。如果完全阅读,ImmutableMultiMap可能是一个不错的选择。

如果你需要并发读/写,那么我将实现我自己的multimap,也许使用ConcurrentHashMap和ConcurrentSkipListSet(你需要小心,因为同步多图和multipmap之间的语义创建了这个使用非阻塞数据结构的方式不同)。如果使用ConcurrentSkipListSet,则可以使用二进制搜索,它比迭代更快。

如果你有很多行,你也可以从使用ConcurrentHashMap和同步列表开始。这可以显着减少争用,这可能足以解决您的性能问题,而且很简单。

答案 3 :(得分:1)

我一直在使用Google Guava作为Apache Commons的替代品......这是Multimap的实现HashMultiMap的一个例子,并注意到地图的值是一个值的集合而不是单个引用。方法&#34;包含()&#34;用于get(key)的结果。

private Multimap<Phase, ResultingState> phaseResults = HashMultimap.create();

/**
 * @param withState is the state to be verified.
 * @param onPhase is the phase to be verified.
 * @return Whether the given result was reported in the given phase.
 */
public boolean wasReported(ResultingState withState, Phase onPhase) {
    return phaseResults.containsKey(onPhase) && phaseResults.get(onPhase).contains(withState);
}

/**
 * @param resultingState is the resulting state.
 * @return Whether the given resulting state has ever been reported.
 */
public boolean anyReported(ResultingState resultingState) {
    return phaseResults.values().contains(resultingState);
}

答案 4 :(得分:0)

当你提到你“迭代所述大型地图挑选匹配的关键字”时,这让我想知道你是否在使用最好的数据结构。有没有办法可以避免这种迭代?

请注意,Guava包含多个具有不同性能特征的多图实现。正如Zwei所提到的,ImmutableMultimap比可变多重映射具有更好的性能。如果您的代码检查multimap是否包含特定值,则SetMultimaps会更快;否则ArrayListMultimap表现更好。