在 Java 中比较字符串的最快方法是什么?

What's the quickest way to compare strings in Java?

提问人:Mediator 提问时间:9/28/2010 最后编辑:Mediator 更新时间:8/28/2016 访问量:45382

问:

在 Java 中比较两个字符串最快的方法是什么?

有没有比等于更快的东西?

编辑: 我无法帮助澄清这个问题。

我有两个字符串,它们按字母顺序排序,大小完全相同

示例:abbcee 和 abcdee

字符串最长可达 30 个字符

Java 字符串

评论

12赞 BoltClock 9/28/2010
为什么对你来说会很慢?equals()
6赞 Bart Kiers 9/28/2010
您是否分析了您的应用程序,并且得出的结论是代码中的热点是由以下原因引起的?如果你还没有分析你的应用,为什么你认为是(或可能是)一个问题?String.equals(...)String.equals(...)
4赞 Sagar 9/28/2010
他的问题并没有说等于是慢的。只是想知道是否有比 equals() 更快的东西。
2赞 KevinDTimm 9/28/2010
他的问题确实指出,当他说“或比等于快的东西”时,等于很慢(或者至少不快)
1赞 Andrzej Doyle 9/28/2010
同意 - 就目前而言,这是一个坏问题。如果你想要比 更快的东西,那么要么你有一些非常具体的性能要求,有测量值的支持(在这种情况下,这些要求必须在给出任何适当的答案之前发布),要么你实际上没有(有不寻常的性能要求),在这种情况下,你应该只使用 equals()。在没有任何理由的情况下暗示“平等还不够快”,这让人们没有什么可做的。equals()

答:

36赞 BalusC 9/28/2010 #1

我不认为 Sun Oracle 还没有将标准优化到最大。所以,我希望它已经是最快的方法。如果您想了解他们是如何实现的,请稍微了解一下它的源代码。摘录如下:String#equals()

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

评论

1赞 mikera 9/28/2010
这对我来说看起来很优化......从理论上讲,可以针对 OP 的特定约束进一步优化它(例如,使用字符串已经具有相等长度的知识,以及字符串中间不同字符的可能性更高),但在实践中显然不能这样做,因为类是最终的,字段是私有的...... +1 挖掘源!
0赞 Stephan 3/24/2012
我不明白为什么他们在进行整个字符串比较之前没有比较哈希码。那会更快。
10赞 BalusC 12/22/2012
@Stephan:那会更低效。循环遍历字符串的所有字符以执行计算。如果 毕竟不一样,那么基本上需要第二次遍历所有字符。hashCode()hashCode()equals()
6赞 Stephan 1/4/2013
@BalusC 这只是事实的一部分。计算完成后,该方法将哈希代码存储到一个 int 中,因此下一次比较将非常快。hashCode()
2赞 Nitsan Wakart 11/9/2016
请注意,String::equals 和许多其他 String 方法是内部函数,由编译器替换为特定体系结构的预烘焙汇编 blob。Java 代码仅在内部函数到位之前就相关,因此几乎从不相关。
3赞 oyo 9/28/2010 #2

这取决于你需要什么。我认为equals()确实经过优化,但也许您需要比equals()更快的东西。看看这篇文章

0赞 mikera 9/28/2010 #3

与往常一样,您需要针对您的应用程序/环境进行基准测试。除非你已经分析并认为这是一个性能瓶颈,否则它可能无关紧要(“过早的优化是万恶之源”)。

话虽如此:

a.equals(b) 对于字符串来说非常快。它可能是 Java 平台中优化最紧密的代码片段之一。如果你能找到任何更快的方法来比较两个任意字符串,我会感到非常惊讶。

一些特殊情况下,你可以作弊并安全地使用 (a==b),例如,如果你知道两个字符串都被隔离了(因此值标识意味着对象标识)。在这种情况下,它可能比 a.equals(b) 略快 - 但这同样取决于编译器/JVM 实现。如果你不知道自己在做什么,很容易搬起石头砸自己的脚......

评论

0赞 mikera 9/28/2010
p.s. 我刚刚对此进行了微基准测试,在我的环境(Sun Java 1.6 上的 Eclipse)中,(a==b) 确实比 a.equals(b) 高出大约 2-4 倍(30ns 对 70-110ns)。YMMV,以及关于微基准测试的通常警告当然适用:-)
0赞 Konrad Rudolph 9/28/2010
查看 @BalusC 发布的实现代码,我完全看不到任何重大优化,根本没有任何东西可以保证您的陈述。诚然,优化这些已经微不足道的代码并不容易。但是低级的,本来可以做的优化是从char-wise转向int-wise比较(显然这需要Java中不容易获得的低级技巧,而且它可能毕竟不会更快)。
0赞 mikera 9/28/2010
嗯,在我看来,它看起来非常严格,例如,他们重新使用字符串长度作为负循环计数器(这是一个经典的低级优化)。我个人看不到可以进行的任何额外优化,除了放弃纯 Java 并下降到专门的本机实现(无论如何 JIT 都有可能这样做......
0赞 Stephan 3/24/2012
您可以安全地使用和实习字符串。该方法会检查标识。equals()equals()
4赞 user207421 9/28/2010 #4

如果你能证明这是一个重大的瓶颈,这会让我感到惊讶,你可以尝试

s1.hashCode() == s2.hashCode() && s1.equals(s2)

它可能会更快一些。它可能不是。

评论

0赞 atamanroman 9/28/2010
这也是我的第一个想法。由于字符串是不互用的(这真的拼写正确吗?),你基本上在这里比较常量整数,这应该很快。只有当对象在大多数情况下都相等时,才可能是一个问题,然后您可以动态交换实现。太可惜了,我在这台机器上没有 jdk,现在很想分析一下。
1赞 Stephan 3/24/2012
是的,它更快。但是您需要事先进行检查。null
1赞 jontro 4/16/2013
我怀疑除非您以某种方式缓存哈希码,否则这会更快。我认为等于比计算哈希码更快。
0赞 Sergey Ponomarev 7/22/2018
两个字符串都应该计算它们的哈希码之前(仅当字符串在哈希映射中用作键时才会发生)也为 0 哈希仍然有效。
0赞 user207421 8/19/2023
@jontro 不是每次调用 时都会计算哈希码。由于它们是不变的,因此它们可以在内部预先计算或缓存,而且确实如此。StringString
28赞 Stephan 3/24/2012 #5

使用哈希码更快地比较相同长度的字符串:

public static boolean equals(final String s1, final String s2) {
return s1 != null && s2 != null && s1.hashCode() == s2.hashCode()
    && s1.equals(s2);
}

你可以测试它,我的结果是 4000000 次比较操作,包括相同、相等和不同的字符串:

String.equals(String):  177081939
equals(String, String):  44153608

注意:计算新字符串对象的 hashCode 需要一些计算时间,然后将 hashCode 存储在该对象中。因此,如果重用字符串对象,我建议的改进只会比默认比较更快。在我的应用程序中,我使用 String 常量并将字符串存储在集合中。使用我的方法对字符串进行多次比较实际上对我来说更快,但可能不是一般的。

如果该方法一直与新字符串一起使用,例如 ,则不会有任何改进。compare("a", "b")

因此,比较字符串的最快方法取决于:

  • 字符串对象是重用(如来自集合)还是始终是新的(如来自输入流)
  • 字符串的长度是否不同
  • 字符串的开头或结尾是否不同
  • 你的编程风格,使用了多少常量
  • 您对 String.intern() 的使用

忽略这些事实,大多数程序都可以使用 String.equals()。

评论

0赞 xchiltonx 12/17/2013
+1 我一直在使用它进行很多“单词运算”,性能太棒了
9赞 Nepoxx 10/23/2014
我认为值得一提的是,可能会有一些哈希码冲突,因此比较哈希值返回误报的可能性非常非常小。这解释了您仍然必须使用等于的事实。出于这个原因,我认为如果你的大多数字符串相等,这会更慢。
1赞 Flow 2/28/2015
你为什么要添加“一定长度”
2赞 vedi0boy 12/17/2015
这怎么更快?您仍然使用 .s1.equals(s2)
0赞 Sumit Kumar Saha 1/11/2016
我相信几乎不会有任何性能改进......欲了解更多信息,请查看 stackoverflow.com/questions/14262431/...
5赞 ungalcrys 10/7/2014 #6

我尝试了不同的组合进行字符串比较(代码在这里):

1. s1.equals(s2)
2. s1.length() == s2.length() && s1.hashCode() == s2.hashCode() && s1.equals(s2)
3. s1.hashCode() == s2.hashCode() && s1.equals(s2);
4. s1.length() == s2.length() && s1.equals(s2);

我使用了长度为 40 个字符的字符串,在 10000000000L 迭代中,在任何迭代之前,我都重新初始化了字符串。

对于我得到的同等蜇伤:

equal: 2873 milis ???
equal: 21386 milis
equal: 7181 milis
equal: 2710 milis ???

对于相同大小的字符串,但最后一个字符不同,我得到了:

different: 3011 milis
different: 23415 milis
different: 6924 milis
different: 2791 milis

对于不同的大小,几乎相同的字符串,但在 S2 的末尾添加了一个字符:

different size: 3167 milis
different size: 5188 milis
different size: 6902 milis
different size: 2951 milis

在我看来,最好先使用 string.length() 比较,然后再使用 equals()。

但这几乎根本不重要,因为在这种情况下,我有 10^10 个字符串与 40 个字符长度进行比较,对我来说奇怪的是,当我首先比较字符串长度时,对于相等的字符串,我有更好的速度。

评论

7赞 Tyler 10/13/2015
我认为你的数据有问题。当您比较相同长度的字符串时,算法 4(比较长度,然后使用 .equals())怎么会比算法 1(仅使用 .equals() 进行比较)更快。对于这些情况,算法 4 正在执行不必要的字符串长度比较,该比较将始终返回 true。
1赞 Flow 2/28/2015 #7

简单的答案

String.equals(Object)

我很确定(这个答案有一些参考),并且 JIT 很可能具有 的内在 for,这意味着它将能够用 JVM 当前运行的架构的特制机器代码替换调用。String#equals