为什么Java的String.equals()方法使用两个计数变量?标杆

2022-09-02 04:35:40

我只是在浏览Java类的实现,以下内容让我感到奇怪:String

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) {               // Here n is being decremented...
                if (v1[i] != v2[i])
                    return false;
                i++;                         // while i is being incremented
            }
            return true;
        }
    }
    return false;
}

这可以很容易地实现,只需一个计数变量,而实际上将是最终的,如下所示:n

while (i != n) {
    if (v1[i] != v2[i])
        return false;
    i++;
}

还有这个,它完全摆脱了:i

while (n-- != 0) {
    if (v1[n] != v2[n])
        return false;
}

它是否与0的比较(一个微小的位)比另一个变量便宜有关,或者是否有任何其他特殊原因可以解释为什么它以这种方式实现?


答案 1

我认为它必须与JDK 7之前的子字符串实现有关。

当时,基础字符数组大小不一定是字符串大小。有两个字段,&(分别是一个)显示底层数组中字符串的位置,因此方法是:offsetcountijthis

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;
}

在上面提到的更改之后,此方法也必须更改,因此它们只是将 n 固定为现在的数组长度:

int n = value.length;

并摆脱了(因为不再有偏移量):j

int i = 0;

现在,因为必须在 2 个用法后递增,所以它在单独的语句中递增:i

if (v1[i] != v2[i])
    return false;
i++;

我认为就是这样,有一个更简洁的实现,如果它是从头开始编写它,但考虑到这是另一个更改推动的更改,那么这是显而易见的......甲骨文人都是普通人,就像我们一样:)

标杆

至于基准测试与)我认为我们必须将苹果与苹果进行比较,因此我将比较2个字符数组的两种方法放在一起:String#equalsArrays#equals(char[], char[]

public static void main(String[] args) {
    final Random random = new Random();
    final int arrays = 10000;
    final int chars = 1000;
    // generate the arrays
    char[][] array = new char[arrays][chars];
    for (int i = 0; i < arrays; i++) {
        for (int j = 0; j < chars; j++) {
            array[i][j] = (char)(random.nextInt(94) + 33);
        }
    }
    // compare using Arrays equals
    long before = System.nanoTime();
    for (int i = 0; i < arrays; i++) {
        for (int j = 0; j < chars; j++) {
            equals_Arrays(array[i], array[j]);
        }
    }
    System.out.println(System.nanoTime() - before);
    // compare using String equals
    before = System.nanoTime();
    for (int i = 0; i < arrays; i++) {
        for (int j = 0; j < chars; j++) {
            equals_String(array[i], array[j]);
        }
    }
    System.out.println(System.nanoTime() - before);
}

private static boolean equals_Arrays(char[] a, char[] a2) {
    if (a == a2)
        return true;
    if (a == null || a2 == null)
        return false;

    int length = a.length;
    if (a2.length != length)
        return false;

    for (int i = 0; i < length; i++)
        if (a[i] != a2[i])
            return false;

    return true;
}

private static boolean equals_String(char[] v1, char[] v2) {
    if (v1 == v2)
        return true;
    if (v1 == null || v2 == null)
        return false;

    int length = v1.length;
    if (length == v2.length) {
        int i = 0;
        while (length-- != 0) {
            if (v1[i] != v2[i])
                return false;
            i++;
        }
        return true;
    }
    return false;
}

在我的盒子上,我没有看到明显的区别。


答案 2

对于大多数带有HotSpot的真实Java VM来说,它不是真正的代码。String.equals是非常重要的方法,它是通过内部函数实现的。它具有特定于平台的本机实现。你可以在这里找到完整的列表 src/share/vm/classfile/vmSymbols.hpp (参见 do_intrinsic)