我一直在努力关注良好的性能和清洁代码。
我很难掌握使用带有150个字符的密钥的HashMap是否合理。
答案 0 :(得分:13)
实际上,150个字符串对于计算hashCode
来说是相对微不足道的。
话虽如此,在这样的情况下,我建议你去测试一下!
创建一个填充HashMap的例程,例如,在此处插入一个代表您的使用场景的大小随机值,其中包含5个字符串作为键。测量需要多长时间。然后对15个字符键执行相同操作,并查看它是如何缩放的。
此外,Java中的字符串是不可变的,这意味着可以为存储在字符串常量池中的每个字符串缓存hashCode
,并且在同一字符串上调用hashCode时不需要重新计算{{1}}宾语。
这意味着虽然您在创建地图时计算较大的哈希码,但在访问时,其中许多哈希码已经预先计算和缓存,使得原始字符串的大小更不相关。
答案 1 :(得分:9)
HashMap密钥的长度是否有不成文的法律?
如果有,它也是未说出口的。我会在一个分析器中测量你的用例,只担心你可以测量的问题,而不是你能想象的可能是一个问题。
将字符串键设为150个字符,这被认为是不好的做法吗?
我对此表示怀疑。
是否会影响性能?在哪个长度?
一切都会影响表现,通常是小到重要,有时甚至是衡量。问题应该是;你需要150个字符键吗?如果你这样做,那就使用它们。
有一种奇特的情况,添加hashCode()为零的字符串是一个坏主意。这是因为在Java 1.0到6中没有优化hashCode为零的用例,并且可以预测拒绝服务攻击。 Java 7通过使用次要的,不太可预测的哈希码来解决这个问题。
答案 2 :(得分:5)
答案很长:快速查看String::hashCode()
的源代码,可以看出在第一次调用后缓存了缓存。同时,如果字符串相等但不相同,则String::equals()
为O(n)(即equals()
为真,但==
为假,因为它们分配在不同的地址)。
因此,您将看到对性能的影响:
在调用HashMap
函数时传递前所未有的字符串。但是,生成大量新字符串本身会影响性能。
使用等于HashMap中已有密钥的字符串键调用HashMap::get()
和HashMap::put()
(因为如果密钥不在集合中,那么很可能只有hashCode ()将被调用。但如果是,则equals()将比较所有字符,直到它确定字符串相等为止。 但是只有传递给这些函数的字符串与HashMap中已经存在的字符不同,因为在这种情况下equals()
非常快。
此外,字符串文字,字符串常量和手动intern()
'字符串连接字符串常量池,其中所有“相等”字符串是具有相同地址的同一对象。因此,如果仅使用此类字符串,hashCode
和equals
非常快。
当然,除非您在紧密循环中执行上述操作(因为150个字符不长且hashCode()和equals()都有效),否则性能影响根本不会引人注意。< / p>
简答:基准。
答案 3 :(得分:2)
首先,没有“不成文的规则”。如果长字符串作为键从算法角度来看是有意义的,请使用它们。如果分析表明存在问题,则优化。
那么 长字符串如何影响哈希表的性能呢?
长字符串比短字符串占用更多内存,并且可能导致可观的更长垃圾收集时间,以及与硬件内存缓存,TLB和(可能)物理相关的其他辅助性能影响记忆页面争用。
String的哈希码算法使用字符串的所有字符,因此其成本与字符串长度成比例。这是通过缓存String哈希码的事实来缓解的。 (第二次及以后在String上调用hashcode
时,您将获得缓存的值。)但是,这只会帮助(此处)使用相同的String对象作为键执行多个哈希表操作。
当您收到哈希冲突时,哈希表会回退到使用String.equals()
来搜索选定的哈希链时比较密钥。在最糟糕的情况下(例如,当字符串为equal
但不是==
时),String.equals()
涉及比较2个字符串的所有字符。
正如您所看到的,这些效果将特定于实际应用,因此很难预测。因此,“不成文的规则”不太可能有所帮助。