为什么String.equals不检查char []值的相等性?

时间:2013-09-18 20:00:14

标签: java string performance micro-optimization

我正在查看java.lang.String的来源,并注意到equals method没有检查支持每个String的char[]是否是同一个对象。这不会改善比较时间吗?

此重写版本中包含的改进:

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;
               /** Begin Optimization **/
               if(v1==v2 && i==j){
                   return true;
               }
               /** End Optimization **/
               while (n-- != 0) {
                   if (v1[i++] != v2[j++])
                       return false;
               }
               return true;
           }
       }
       return false;
   }

我认为,如果使用String.substring获得两个字符串,甚至可能实际使用字符串,这可以提高性能。

有人知道他们选择不以这种方式实施的原因吗?

更新:对于可能对implementation of String可能不太了解的任何人,除了String池之外还有两个String对象可以具有相同的char []值的情况,int offset和int count。

请考虑以下代码:

String x = "I am a String, yo!";
String y = x.split(" ")[3];
String z = x.substring(7,14);

你最终会遇到这样的情况: Debugger Expressions

显然,在Java 7u6中,Strings的价值共享功能已被废除,以满足一些基准测试。因此,如果您花时间使用String.substring()而不是字符串连接来使代码在适当的时间(或根本)运行,那么您就是SOL。

4 个答案:

答案 0 :(得分:1)

嗯,您需要检查char[]offset count(字符串长度)。由于char[]仅在String类中创建,因此所有这三个相等的唯一方法是String从自身创建doppelgänger。你可以这样做(例如new String("why?")),但这不是一个常见的用例。

<speculative>我甚至不确定它是否能加快速度。在绝大多数情况下,检查将失败,这意味着它正在做额外的工作,没有任何好处。这可以通过分支预测来抵消,但是在 情况下,检查通过的次数很少,它将使该分支预测所做的猜测无效,这实际上可能会使事情变慢。换句话说,如果JVM / CPU尝试针对常见情况进行优化,那么您通常什么也得不到,并且在极少数情况下(实际上您正在尝试优化)实际上会让您自己受伤。如果它没有尝试优化这种常见情况,那么为了进行相当罕见的比较,你会在大多数比较中伤害自己。 </speculative>

答案 1 :(得分:0)

在Java 7中see this article),substring()不再对返回的String使用相同的后备数组。你仍然需要检查每个角色。基本上,String支持char[]永远不会共享,因此您无法

this.value == other.value

答案 2 :(得分:0)

我不明白这个问题 char[]String的内部成员。如果2个String引用是相同的(应该是因为你应该使用实习字符串)char []将是相同的 但是对于不同的实例,为什么你期望char[]是相同的参考?字符串是不可变的,并且2个不同的String对象不可能共享对同一个支持的数组的引用 另外,即使对于子串也使用这种条件检查甚至没有意义 我不知道其中一个答案中提到的Java 7的变化,但在这种情况下检查备份阵列的相等性是错误的。
String对象不仅是后备数组,还有当前的偏移量,长度等 因此,作为子字符串结果的2个String对象可以由相同的char数组支持,但可以很好地包含不同的(子)字符串作为内容 - 不同相同的字符数组

中的偏移

答案 3 :(得分:0)

对后备字符数组进行此类检查很可能是多余的,不是必需的。

有两个实例,后备字符数组对象可以是相同的对象(因为其他指向子字符串方法总是创建一个新的后备字符数组)。

定义字符串文字

String a = "Hello";
a.equals("Hello"); // Backing array of "Hello" string literal 
                   // will be same as that of variable a

在这种情况下,即使在检查后备字符数组之前,equals方法也会确定String在以下行中是否相等。

if (this == anObject) { // From String.equals method
    return true;
}

使用String copy构造函数创建另一个String对象

请注意,以下代码块没有实际价值,因此无法在实际代码中完成。

String a = "Hello;
String b = new String(a);
a.equals(b);

因此,如果String对象不同,可以安全地假设它们总是不同的,而不是进行额外的检查以确定字符数组是否相同。