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

时间:2016-03-14 16:55:39

标签: java string traversal

我只是在查看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比较(比微小比特)比另一个变量更便宜,还是有任何其他特殊原因来解释为什么以这种方式实现?

3 个答案:

答案 0 :(得分:10)

我认为必须使用JDK 7之前的substring实现。

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

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;

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

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

我认为就是这样,有一个更简洁的实现,如果它是从头开始编写它是明显的,但鉴于它是由另一个变化推动的变化......甲骨文人是普通人,就像我们一样:)

基准

至于基准String#equals vs Arrays#equals(char[], char[])我认为我们必须将苹果与苹果进行比较,因此我将两种方法用于比较2个字符数组:

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

在我的盒子上,我看不出显着的差异。

答案 1 :(得分:4)

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

答案 2 :(得分:2)

这不是一个答案,只是一个伪banchmark:

public class CompString {

    public static void main(String[] args) {

        DecimalFormat df = new DecimalFormat("0.#################");
        int count = 1000000;
        int length = 20;
        int runs = 10;

        String[] strings = new String[count];
        String[] copiedStrings = new String[count];
        char[][] chars = new char[count][];
        char[][] copiedChars = new char[count][];

        for (int i = 0; i < count; i++) {
            String str = RandomStringUtils.random(length);

            strings[i] = new String(str);
            copiedStrings[i] = new String(str);

            chars[i] = Arrays.copyOf(str.toCharArray(), str.length());
            copiedChars[i] = Arrays.copyOf(chars[i], chars[i].length);
        }

        System.out.println("Lets banchmark that !!");
        int loop = 0;
        while (loop++ < runs) {
            System.out.println("Run #" + loop);
            long t = 0;
            long t0 = System.currentTimeMillis();
            for (int i = 0; i < count; i++) {
                strings[i].equals(copiedStrings[i]);
            }
            long t1 = System.currentTimeMillis();
            t = t1 - t0;

            System.out.println("Avg String.equals() duration: " + df.format(t / (double) count));

            t0 = System.currentTimeMillis();
            for (int i = 0; i < count; i++) {
                Arrays.equals(chars[i], copiedChars[i]);
            }
            t1 = System.currentTimeMillis();
            t = t1 - t0;

            System.out.println("Avg Arrays.equals(char[] char[]) duration: " + df.format(t / (double) count));
            System.out.println();
        }
    }
}

以下是结果:

Run #1
Avg String.equals() duration: 0,000017
Avg Arrays.equals(char[] char[]) duration: 0,000013

Run #2
Avg String.equals() duration: 0,000037
Avg Arrays.equals(char[] char[]) duration: 0

Run #3
Avg String.equals() duration: 0,000002
Avg Arrays.equals(char[] char[]) duration: 0

Run #4
Avg String.equals() duration: 0,000003
Avg Arrays.equals(char[] char[]) duration: 0

Run #5
Avg String.equals() duration: 0,000002
Avg Arrays.equals(char[] char[]) duration: 0

Run #6
Avg String.equals() duration: 0,000003
Avg Arrays.equals(char[] char[]) duration: 0

Run #7
Avg String.equals() duration: 0,000003
Avg Arrays.equals(char[] char[]) duration: 0

Run #8
Avg String.equals() duration: 0,000003
Avg Arrays.equals(char[] char[]) duration: 0

Run #9
Avg String.equals() duration: 0,000002
Avg Arrays.equals(char[] char[]) duration: 0

Run #10
Avg String.equals() duration: 0,000002
Avg Arrays.equals(char[] char[]) duration: 0

因为String.equals比较底层数组(使用单个迭代器变量)需要更多时间,我们可以在这里排除一些神奇的JVM优化问题(但它可能在过去吗?)。