【问题标题】:Are String hashCode in java pre-computed?java中的String hashCode是预先计算的吗?
【发布时间】:2013-07-10 11:36:29
【问题描述】:

我说的是Java。

String str= "abcd";
str.hashCode(); 

我的问题是哈希码是什么时候计算的? @ 第 1 行还是 @ 第 2 行?

我假设,哈希码是预先计算的。每当更新字符串时,哈希码也会“可能”更新。

或者它的另一种方式,即每次你调用str.hashCode()时,java都会使用here描述的公式计算它。

Consistency of hashCode() on a Java string

【问题讨论】:

  • 您不能将现有的String 实例更新为String is immutable。您将能够做的是创建一个新实例并将其分配给相同的变量。
  • 之前,在阅读文档之前,我的错误印象是 1) 如果两个字符串不相等,java.lang.String.hashCode() 将永远不会为两个字符串返回相同的 hashcode()。 2)我仍然认为在 java Strings hashcode 中是预先计算的。然而,除了暗示它总是计算但不想假设的公式之外,文档没有提到任何其他内容。
  • 好吧,正如您从我发布的摘录中看到的那样,至少在 Sun JVM 中它是延迟计算和缓存的。

标签: java hashcode


【解决方案1】:

字符串不能被更新,因为它们是不可变的,并且值在计算一次后被缓存:

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

【讨论】:

  • 值得注意的是(据我所知)可变对象上的哈希码也会被兑现,并且不会在更新时重新计算
  • 与许多其他事情一样,这是一些 Java VM 的实现特定细节。可以在创建字符串实例时计算哈希码,可以为每次调用hashCode() 计算它,也可以在第一次调用hashCode() 时计算它并缓存以供以后使用。所有三个变体都满足 API 合同的要求。
  • 那是合理的。但是由于 String 作为 HashMap 的键是一个非常受欢迎的选择,如果它没有被缓存,那将是一个重大的疏忽......
  • @jarnbjo 谢谢。也许这就是我在文档中找不到任何内容的原因。
  • 我认为字符串具有跟踪其偏移量和长度的字段,以加快子字符串操作;在后来的 Java 版本中是否省略了这些字段?还剩下哪些字段?
【解决方案2】:

您不能“更新”字符串。字符串是不可变的。当你“改变”一个字符串实例时,你实际上得到了一个新的字符串实例。

【讨论】:

  • 您无法更改字符串 instance - 您可以将 reference 更改为字符串以指向新实例。
  • “当你‘改变’一个字符串实例时……”这句话具有误导性。您不能更改字符串实例,并且为引用分配新值不一定“获得新的字符串实例”。
  • 是的,但这并不能回答问题。
【解决方案3】:

您不能将现有的String 实例更新为String is immutable。您将能够做的是创建一个新实例并将其分配给相同的变量。

【讨论】:

  • 你说的很对,但这并不能回答问题,即String哈希码是否是惰性计算的。
【解决方案4】:

String.hashCode() 方法仅在您调用它时触发,就像您在 Java 中编写或使用的任何其他方法一样。

所以,为了回答您的问题,创建字符串时不会计算哈希码。

如果你看到 hashcode 方法,它被编写为只计算一次,并将预先计算的 hashcode 值保存到局部变量 hash

public int hashCode() {
 int h = hash;
 if (h == 0) {
  int off = offset;
  char val[] = value;
  int len = count;
  for (int i = 0; i < len; i++) {
   h = 31*h + val[off++];
  }
  hash = h;
}
return h;
} 

所以,hashcode只有在方法被调用时才会计算

【讨论】:

    【解决方案5】:

    对于大多数字符串,哈希值将在第一次调用 hashCode() 时计算,或者在多线程场景中——每次调用它,直到其中一个调用设法计算并缓存它的值。但是,包含哈希码为零的字符序列的字符串实例将在每次调用 hashCode() 时计算其哈希值。请注意,可以通过使用哈希函数来避免此类冗余计算,该哈希函数对于非平凡字符串永远不会返回零(例如,通过添加行 if (!h) h=value.length+1;),但现在更改 Java 来做到这一点可能会——在至少在理论上 - 破坏一些现有代码,这些代码假定内置字符串 hashCode() 曾经产生零的任何字符序列将继续这样做,直到时间结束。

    因为写入String 类型的变量只会覆盖包含的引用,而不会影响它之前引用的String 的实例,因此与之前字符串的哈希码无关(包括它的值是否具有尚未计算)将受到分配的影响。

    【讨论】:

      【解决方案6】:

      我看到这个问题仍然“没有回答”。通过谷歌搜索找到它。 因此,总结所有答案并添加更多价值:

      • 字符串的hashCode在第一次调用这个函数时被缓存,有一个内部字段hashCode是零初始化的
      • 字符串是不可变的,但您可以使用反射 API 更改它们。缓存的 hashCode 仍然不会自动更新
      • 由字符常量初始化的字符串总是被保留的,所以如果你会写

        public static void main(String[] args) {
            String hello1 = "Hello";
            String hello2 = "Hello";
            System.out.println( hello1 == hello2 );
        }
        

      您可以确定它会打印出“true”。如果您调用 hello1 和 hello2 的 hashCode,则 hashCode 将只计算一次,因为它实际上是同一个对象。

      希望这些信息对那些通过谷歌搜索到达那里的人有用。

      【讨论】:

        猜你喜欢
        • 2020-01-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-23
        • 1970-01-01
        • 2018-03-03
        相关资源
        最近更新 更多