当Key保证唯一时,HashMap的性能

时间:2011-07-12 12:42:17

标签: java performance hashmap concurrenthashmap

如果我希望使用的密钥保证是唯一的(或者至少可以假设密钥是唯一的),那么使用'vanilla'ConcurrentHashMap可以提供最佳性能,还是需要修改散列函数或put方法以避免不必要的散列?

此外,数字键是否比非数字键具有任何性能优势(例如具有适当散列函数的String或POJO)?

5 个答案:

答案 0 :(得分:7)

正如评论中已经提到的,如果您不需要线程安全方面,请不要使用ConcurrentHashMap

如果您希望获得绝对最佳性能,请考虑实习您的密钥并使用IdentityHashMap。这避免了计算对象的哈希值(并且,如评论中所述,否定了对equals进行求值的需要),而是假设引用本身就是哈希值。

请注意,您必须确保同一个键的两个实例是同一个对象(例如,您必须确保引用相等,而不仅仅是对象相等)。实习所有密钥是实现这一目标的一种方法。

  

实施说明:这是一个简单的线性探测哈希表,如Sedgewick和Knuth的文章中所述。阵列交替显示保持键和值。 (对于大型表,这比使用单独的数组具有更好的局部性。)对于许多JRE实现和操作混合,此类将产生比HashMap更好的性能(HashMap使用链接而不是线性探测)。

如果您知道所有密钥,也许您也可以考虑perfect hashing?或映射到一个简单的数组结构?

答案 1 :(得分:1)

ConcurrentHashMap是HashMap实现中最昂贵的,这是因为它是线程安全的。

所有地图必须具有唯一键,因此这是给定的。

如果使用支持TLongHashMap等原始数据的集合,则使用数字具有性能优势,但使用自定义哈希映射可能会更快。

来自http://vanillajava.blogspot.com/2011/07/low-gc-in-java-using-primitives.html

Test                                    Performance Memory used
Use Integer wrappers and HashMap        71 - 134 (ns)   53 MB/sec
Use int primitives and HashMap          45 - 76 (ns)    36 MB/sec
Use int primitives and FastMap          58 - 93 (ns)    28 MB/sec
Use int primitives and TIntIntHashMap   18 - 28 (ns)    nonimal
Use int primitives and simple hash map   6 - 9 (ns)     nonimal 

答案 2 :(得分:1)

  

如果我希望使用的密钥保证是唯一的(或者至少可以假设密钥是唯一的),那么使用'vanilla'ConcurrentHashMap可以提供最佳性能,

如果ConcurrentHashMap是潜在的并发瓶颈,通常会使用Map。如果您的应用程序是单线程的,或者没有争用,ConcurrentHashMapHashMap慢。

  

或者是否需要修改散列函数或put方法以避免不必要的散列?

散列函数每哈希表的“探测”得到一次评估;例如每getput次操作一次。您可以通过缓存结果来降低散列函数的成本,但这会使每个密钥对象额外增加4个字节的存储空间。缓存是否值得进行优化取决于:

  • 将散列的相对成本与应用程序的其余部分进行比较,
  • 实际使用缓存值的hashCode()调用的比例。

这两个因素都是高度针对特定应用的。

(顺便说一句,使用身份哈希码作为哈希值的长期成本额外的4个字节的存储空间。)

  

此外,数字键是否比非数字键具有任何性能优势(例如具有适当散列函数的String或POJO)?

哈希函数在数值情况下可能更便宜,但它是否值得,取决于是否存在使用数字键的特定于应用程序的缺点。而且,如上所述,相对成本是应用程序细节。例如,String.hashCode()的成本与被散列的字符串的长度成正比。

答案 3 :(得分:0)

Java的HashMaps最终由Entry<K,V>数组支持,其中K的哈希码用于确定存储Entry的数组中的插槽。

所使用的数组的大小(通常从16开始)远小于可能的哈希码(2 ^ 32~ = 40亿)的数量,因此即使哈希码是哈希码,也必然会在此数组中发生冲突。唯一的。

只要你的hashcode()方法很快,用作Key的类型就没有区别。请记住,hashcode()方法可能被称为 lots 次,所以如果它很慢,你可以在内部将它缓存在对象中。

答案 4 :(得分:0)

我有ConcurrentHashMap实例映射,可通过multithread.seeing在代码片段下访问。这些怎么样?

Iterator<String> it = new TreeSet<String>(map.keySet()).iterator();
            while(it.hasNext())
            {
                id = it.next();
                synchronized(map)
                {
                    msg = map.get(id);
                    if(msg != null)
                        map.remove(id);
                }
                if(msg != null)
                listener.procMessage(msg);
            }
相关问题