Java字符串哈希码缓存

5

String的不可变性之一的优点是哈希码缓存,以便更快地访问。

  • 在这种情况下,具有相同哈希码的字符串如何处理缓存?
  • 这种情况下它真的可以提高性能吗?

在这种情况下,具有相同哈希码的字符串如何处理缓存?
  • 它在字符串对象本身的级别上进行缓存,而不是某个外部缓存。
- Petro Semeniuk
5个回答

8
在这种情况下,对于具有相同哈希码的字符串,缓存是如何处理的?
被缓存的是字符串的哈希码。它被缓存在字符串本身的私有int字段中。不同的字符串可能具有相同的哈希码并不重要......因为哈希码存储在各自的字符串对象中。
最重要的是,具有相同字符序列(因此相等)的两个字符串具有相同的哈希码值。这是保证的,因为Java字符串的哈希码算法是标准化的...并且具有此属性。
那么在这种情况下真的会提高性能吗?
平均而言,是的,特别是当字符串长度变大时,效果更好。
一个良好的字符串哈希码算法需要查看字符串中的每个字符......否则,类似的字符串可能会系统地映射到相同的哈希码(这是不好的)。避免多次查看这些N个字符是一个巨大的优势。
唯一重要的不适用缓存的情况是:
当大多数字符串哈希码仅使用一次时;或者
当大多数字符串非常短。
(还有一种非常晦涩的情况。如果一个String的哈希值为0,则缓存将无效。这是因为String类使用0来表示哈希码未被缓存。)

小注:据我所知,String equals 首先查找哈希码,然后查找相等的字符串长度,最后再查找每个字符。 - user unknown
这并不适用于Oracle Java 7,因为在equals(Object)中根本不使用hashcode值。我不知道其他/旧版本的Java... - Stephen C
没错,对于Java-6也是一样的:它不使用哈希码,但会检查a)引用标识、b)类型标识、c)大小和d)每个字符。 - user unknown
就我个人而言,缓存的哈希码值不被使用的最有可能的原因是,大多数情况下,在调用equals方法时字符串的哈希码尚未计算出来... 而且以后也不会被使用。Sun/Oracle的Java开发人员进行了大量的代码分析和测试,以确定哪些优化实际上在实际生活中是有效的。 - Stephen C
你确定“哈希值为零”的评论吗?有一些字符串的哈希码必须每次重新计算,这是正确的吗?我没有查看String的实现,但我查看过一些类似的类,它们只是简单地编写了代码:“它是否为零?那么哈希码就是1,以避免每次重新计算。”除非你的HashSet接近Integer.MAX_VALUE个桶,否则不会导致额外的哈希桶冲突。 - Theodore Murdock
@TheodoreMurdock - 是的,我确定。我查看了源代码。 - Stephen C

4
在这种情况下,对于具有相同哈希码的字符串,缓存是如何处理的?
我不理解你问题的第一部分。无论哈希码是否相同(因为两个不同的字符串理论上可以具有相同的哈希码,所以如果哈希码相等,并不意味着字符串相等),所有字符串的缓存都是相同的处理方式。但是,如果使用相同的字符串对象,则无需重新计算哈希码,因为它已被缓存。
这真的会提高性能吗?
毫不含糊地是“是”。

2
缓存只是字符串对象内的一个int字段。多个字符串可以具有相同的哈希码而不会出现问题。
这显著提高了性能,因为:
- 计算哈希码比读取单个int字段更昂贵 - 如果你计算一次字符串的哈希码,很可能你还需要计算字符串的哈希码很多次(例如,它被用作哈希表键)
如果您有兴趣,值得查看源代码:

http://www.docjar.com/html/api/java/lang/String.java.html


1
在大多数情况下,只有在尝试将字符串放入HashMap时才会计算hashCode。然后,Map.Entry会缓存它以加快比较和重新散列的速度。

事实上,由于几乎所有字符串都不是哈希映射中的键,因此缓存字段是无用的,并且每个字符串实例需要额外的8个字节。 - Sergey Ponomarev
1
实际上,Java哈希码是32位的。 - Zdeněk Pavlas

-1

对于第一个问题,它取决于您的哈希策略。例如,如果您将单词中所有字母的ASCII代码相加以获得该字母的哈希代码(a的代码为65,A的代码为97),在这种情况下,单词“abc”和“bca”的哈希代码相同。

对于第二个问题,它也取决于您的哈希策略,但在大多数情况下,答案是肯定的。


2
不确定第二个“可能依赖”是什么意思。无论哈希策略如何,如果自缓存意味着它只针对给定的字符串对象计算一次,那么它总是更快的。 - Hovercraft Full Of Eels
如果缓存意味着只计算一次,那么我同意你的说法。英语不是我的母语,所以……缓存意味着只有一次? - wyp
好的 - 哈希算法是在String类中实现的,而不是某个Siva策略的对象。这已经固定了。 - user unknown
@wyp在这个上下文中,我不知道缓存还可能意味着什么。 - user207421
-1,这不是Java String.hashCode()的工作方式 - 忽略重排将是一个明显糟糕的哈希函数。 - poolie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接