【问题标题】:Does String.intern() really increase performance?String.intern() 真的能提高性能吗?
【发布时间】:2014-05-19 22:58:27
【问题描述】:

我做了一些调查以了解String.intern() 方法是如何在java 中实现的。

我查看了 Open JDK 6 中实习生池的 C++ 实现,在那里我看到了一个简单的HashSet。对我来说,这意味着当有人试图实习 String 时,应该完成以下步骤:

  1. 查找与给定String关联的哈希码
  2. 找到合适的存储桶
  3. 将给定的字符串与存储桶中的所有其他字符串进行比较。 在此步骤之前可能有 0 个字符串、一个字符串或很多 桶中的字符串。因此,如果给定的 String 以前是 放入桶中,我们将得到至少一个比较(即 最好的情况。当然可能有很多碰撞和 现在还有许多其他字符串在桶中)
  4. 如果在存储桶中找到了字符串,那么它应该是 由intern() 方法返回
  5. 如果未在桶中找到字符串,则应将其放入 在桶中并由intern() 方法返回

很多人说str1.intern() == str2.intern() 会比str1.equals(str2) 快。

但我看不出它应该更快的原因。

正如我所见,在str1.equals(str2) 的情况下,我们总是有两个字符串在String.equals() 方法中逐个字符地进行比较。

str1.intern() == str2.intern() 的情况下,我们需要进行多少次比较或将字符串放入/取出池(对,可能有很多比较,而且它们也是简单的逐字符比较)?

所以在str1.intern() == str2.intern() 的情况下,即使我们使用== 来比较字符串,我们也会有许多额外的操作,例如前面描述的比较。

当我理解它时,我决定进行一些基准测试。

第一个结果告诉我str1.intern() == str2.intern()str1.equals(str2) 快。

这种行为是由于String.intern() 方法是本机的,因此不应每次都对其进行解释,而String.equals() 是一个java 方法。

于是我决定使用-Xcomp 选项让JVM 在启动时编译所有代码。

在那之后等于展示了比实习生更好的速度。

我在 Java 6 和 7 上对其进行了测试。

所以我的问题是你有没有遇到过提高字符串比较速度的情况?我是的,怎么可能?

或者intern()可能只能帮助节省更多的空闲内存?

【问题讨论】:

  • str1.intern() == str2.intern() - 不!你应该已经把字符串实习了。在比较站点实习是纯粹的开销。 (当你正确使用它时,实习是否有用还有待商榷,但这样的实习是没有用的。)
  • 我不认为这完全回答了这个问题,我也没有手边的参考资料,但我记得很久以前读过 String.hashCode 方法已针对非常好的分布进行了优化,例如在哈希表中,您将很少遇到冲突。
  • “人们说”绝不是做任何事的好理由。 +1 进行自己的研究。
  • +1 用于实际测试以回答“X 比 Y 快”的问题。
  • +1 很好的研究,一个有趣的问题!

标签: java string string-interning


【解决方案1】:

String.intern() 旨在减少内存的使用。

仅当内存中有许多相同字符串的多个副本时,才使用实习字符串(如果有的话)。通过实习字符串,所有这些副本都将使用相同的引用。

只有当我拥有 数百万 个相同字符串的副本时,我才看到实习字符串会有所帮助。

与任何类型的优化一样,只有在出现性能或内存问题并且您已对其进行分析以检测到这是瓶颈之后才执行此操作。

See this Blog post 了解有关字符串实习的更多详细信息。

【讨论】:

  • 例如,我曾经看过一个生产服务器的堆转储,它经常出现堆空间问题。我发现 8 MB 的字符串包含值“0”和“1”。这是使用intern 的绝佳机会。
  • @Brandon 这听起来更像是数据类型的错误选择。在这种情况下,boolean 不是更好的选择吗,在需要字符串表示时使用String.valueOf
  • 同意 - 回顾源代码,我找不到我在哪里使用 intern 作为那个例子,但我看到一堆 Boolean.valueOfInteger.valueOf 等。我想什么我最终得到interning 是属性名称。 “0”和“1”等是从数据库加载的属性值,并且有一小部分固定的属性集经常加载不同的值。
【解决方案2】:

关于为什么str1.intern() == str2.intern() 可能更快的问题:

这是 String.equals() 实现 - 正如您所看到的,根据比较的字符串,它可能非常低效。

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String) anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                        return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

您的步骤可能会快很多:
1) hashCode() 对任何 String 计算一次,因为它是不变的,而且相当快
2) 找到桶是 O(1)
3) 将您的 String 与同一个桶中的其他人进行比较 - 可能有一些但仍然应该比 equals()
更快 4) 和 5) 很快

并且不要忘记,对于任何字符串,上述操作都必须只执行一次,一旦它是intern()ed,就会从第一次比较中返回结果。

【讨论】:

  • "不要忘记,对于任何字符串,上述操作只需要执行一次,一旦它被 intern()ed,结果就会从第一次比较中返回。"我可能错过了一些东西,让我们假设我们有 str1.intern() 然后 str1.intern() 再次。我认为第一行会将字符串放入池中(进行所有步骤),然后第二行将执行相同的步骤(找到一个桶,将字符串与桶中的其他字符串进行比较)以在池中找到这个字符串并将其退回。据我记得这是我在 C++ 代码中看到的。
  • 或者你的意思是我们可以做 s1 = str1.intern(); s2 = str2.intern() 然后我们将能够执行 s1==s2 一百万次并且性能会比百万等于比较的情况更好?因为我认为如果我们有一百万个 str1.intern()==str2.intern(),我们每次迭代都会与 HashSet 进行大量交互。
  • 是的,您的第二条评论就是我的意思。当您调用 intern() 时,保留一份副本并在其他任何地方使用它是有意义的......
  • 如果您使用intern(),这意味着至少有一个字符串来自“其他地方”,例如从文件中读取。在这种情况下,您将支付为每个字符串计算哈希码的成本,这需要相同的时间来逐字节比较两个字符串。
  • @kdgregory 如果您只使用所有这些字符串一次,则无需考虑和/或改进。另一方面,如果您需要比较两个以上的字符串,您会突然发现很大的改进。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-02
  • 2016-07-12
  • 1970-01-01
  • 2013-04-12
  • 1970-01-01
相关资源
最近更新 更多