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?
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.
== 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.
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.
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).
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 :)
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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With