String IdentityHashMap vs HashMap性能

时间:2015-04-11 15:46:14

标签: java collections hashmap hashtable

Identity HashMap是java中的特殊实现,它比较对象引用而不是equals,并且还使用identityHashCode而不是hashCode。此外,它使用linear-probe hash table代替Entry list

Map<String,String> map = new HashMap<String,String>(); 

Map<String,String> iMap = new IdentityHashMap<String,String>();

对于字符串键是否意味着如果正确调整,IdentifyHashMap通常会更快?

添加了一些基本代码

public class Dictionary {

public static void main(String[] args) throws IOException {

    BufferedReader br = new BufferedReader(new FileReader("/usr/share/dict/words"));

    String line;
    ArrayList<String> list = new ArrayList<String>();


    int index=0;
    while( (line = br.readLine()) != null){
        list.add(line);
    }
    System.out.println("list.size() = " + list.size());
    Map<String,Integer> iMap = new IdentityHashMap<String,Integer>(list.size());
    Map<String,Integer> hashMap = new HashMap<>(list.size());

    long iMapTime=0,hashMapTime=0;

    long time=0;
    for(int i=0; i< list.size(); i++){
        time= System.currentTimeMillis();
        iMap.put(list.get(i),i);
        time = System.currentTimeMillis()-time;
        iMapTime += time;
        time= System.currentTimeMillis();
        hashMap.put(list.get(i),i);
        time = System.currentTimeMillis()-time;
        hashMapTime += time;
    }

    System.out.println("iMapTime = " + iMapTime + " hashMapTime = " +hashMapTime);

}

}

尝试了非常基本的性能检查。我正在读字典单词(235K)&amp;推进这两个地图。它打印在下面。

list.size() = 235886
iMapTime = 101 hashMapTime = 617 

我认为这是非常好的改进,除非我在这里做错了。

4 个答案:

答案 0 :(得分:4)

您将在IdentityHashMap上看到明显更快的性能,但需要付出相当大的代价。

您必须绝对确定您永远不会在地图中添加具有相同值但身份不同的对象。

现在和将来都难以保证,很多人都会做出错误的假设。

例如

String t1 = "test";
String t2 = "test";

t1==t2将返回true。

String t1 = "test";
String t2 = new String("test");

t1==t2将返回false。

总体而言,我的建议是,除非你绝对急需提升性能,并确切知道自己在做什么,并严格锁定并评论对课程的访问权限,然后使用IdentityHashMap,你将面临很大的风险,很难跟踪将来的错误。

答案 1 :(得分:3)

IdentityHashMap<String,?>如何运作?

要使IdentityHashMap<String,?>适用于任意字符串,您必须String.intern() put()以及传递给get()的潜在密钥。intern()。 (或使用等效机制。)

注意:与@ m3th0dman的答案不同,您不需要put()这些值。

无论哪种方式,实习字符串最终都需要在已经实现的字符串的某种哈希表中查找它。因此,除非你因为某些其他原因(因此已经支付了费用)而不得不实习你的琴弦,否则你将无法获得实际的性能提升。

那么为什么测试表明你可以?

如果您的测试不切实际,那么您保留与LinkedHashMap一起使用的密钥的确切列表,并按列表顺序逐个迭代它们。注意(可以通过将元素插入iterator()并在其条目集上简单地调用IdentityHashMap来实现相同的目的。

那么equals()的重点是什么?

在某些情况下,保证(或实际保证)对象标识与ThreadLocal相同。想象一下,尝试实现自己的public final class ThreadLocal<T> { private final IdentityHashMap<Thread,T> valueMap; ... public T get() { return valueMap.get( Thread.currentThread() ); } } 类,例如,你可能会写这样的东西:

{{1}}

因为你知道线程没有超越身份的平等概念。如果你的地图键是枚举值,那么同样如此。

答案 2 :(得分:1)

从技术上讲,你可以这样做,以确保你有相同的字符串表示实例:

public class StringIdentityHashMap extends IdentityHashMap<String, String>
{
    @Override
    public String put(String key, String value)
    {
        return super.put(key.intern(), value.intern());
    }

    @Override
    public void putAll(Map<? extends String, ? extends String> m)
    {
        m.entrySet().forEach(entry -> put(entry.getKey().intern(), entry.getValue().intern()));
    }

    @Override 
    public String get(Object key)
    {
        if (!(key instanceof String)) {
            throw new IllegalArgumentException();
        }
        return super.get(((String) key).intern());
    }

    //implement the rest of the methods in the same way
}

但是这对你很有帮助,因为intern()调用equals()来确保字符串池中存在或不存在给定的String,这样你最终会得到典型的HashMap

然而,这只会帮助您改善内存而不是CPU。没有办法实现更好的CPU使用率并确保你的程序是正确的(没有可能使用JVM的一些内部知识可能会改变)因为字符串可以在字符串池中,你不知道它们是否在没有(不是隐含地)调用equals()

答案 3 :(得分:1)

有趣的是,IdentityHashMap可以是SLOWER。我使用Class对象作为键,并在IdentityHashMap上看到HashMap的性能提升约50%。

IdentityHashMap和HashMap在内部是不同的,所以如果你的键的equals()方法真的很快,那么HashMap似乎更好。