Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Java's String.equals() method use two counting variables?

Tags:

I was just looking through the implementation of Java's String class and the following struck me as odd:

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

This could easily be implemented with just one counting variable while n would be effectively final, like this:

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

Theres also this, which gets rid of the i entirely:

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

Does it have to do with comparison to 0 being (a miniscule bit) cheaper than to another variable or is there any other particular reason as to why it is implemented that way?

like image 450
Marv Avatar asked Mar 14 '16 16:03

Marv


People also ask

What does the equals () method do in strings?

Java String equals() Method The equals() method compares two strings, and returns true if the strings are equal, and false if not. Tip: Use the compareTo() method to compare two strings lexicographically.

When would you use == vs equal () and what is the difference?

== is a relational operator which checks if the values of two operands are equal or not, if yes then condition becomes true. equals() is a method available in Object class and is used to compare objects for equality.

Why we use double equal to in Java?

equals() is a built-in function in java that compares this object to the specified object. The result is true if and only if the argument is not null and is a Double object that contains the same double value as this object. It returns false if both the objects are not same.

Why is it important to write the equals () method when writing a class?

The reason the equals method in the Object class does reference equality is because it does not know how to do anything else. Remember, every class in Java is an Object (via inheritance).


2 Answers

I think it has to to with the substring implementation prior to JDK 7.

At the time, the underlying character array size was not necessarily the string size. There were two fields, offset & count (i an j respectively) that were showing where is this string in the underlying array, so the method was :

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

After the change mentioned above, this method had to be also changed, so they just fixed n to be now the array length:

int n = value.length;

and got rid of j (because there's no offset anymore):

int i = 0;

Now because i has to be incremented after 2 usages, it is incremented in a separate statement :

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

I think that's it, there's a more concise implementation which is evident if it's to write it from scratch but given that it was a change pushed by another change ... Oracle people are ordinary people just like us :)

Benchmarking

As for benchmarking String#equals vs Arrays#equals(char[], char[]) I think we have to compare apples with apples, so I've put the two approaches for comparing 2 character arrays together :

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

On my box I see no notable difference.

like image 135
Bax Avatar answered Oct 14 '22 08:10

Bax


It is not real code for most of real Java VM with HotSpot. String.equals is very important method and it is implemented through intrinsic. It has platform specific native implementation. You can find full list here src/share/vm/classfile/vmSymbols.hpp (see do_intrinsic)

like image 28
sibnick Avatar answered Oct 14 '22 06:10

sibnick