在我的代码中,我有一张地图,在几秒钟内被大量使用了几千次。最初我有一个TreeMap,但在测试9,000个条目时,我看到我的旧处理器融化了。这需要扩大规模。所以我转移到了HashMap,性能非常出色。
现在我正在改变我的设计,正在寻找一个MultiMap。但是我害怕对get()
方面的性能影响,因为它必须遍历所述大型地图,挑选匹配的密钥,并且当被调用很多次甚至同步时,它似乎会很慢。
是否有一个好的MultiMap可以处理如此大的值并具有出色的性能?性能在这个应用程序中至关重要,因为可能有许多大型单独的映射处理非常大的工作负载,使“小”性能损失成为非常大的问题。
如果可以提取单独工作而没有任何依赖性,则获得奖励积分。
答案 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表现更好。