我只是在查看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比较(比微小比特)比另一个变量更便宜,还是有任何其他特殊原因来解释为什么以这种方式实现?
答案 0 :(得分:10)
我认为必须使用JDK 7之前的substring实现。
当时,基础字符数组大小不一定是字符串大小。有两个字段offset
& count
(分别为i
和j
)显示基础数组中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优化问题(但它可能在过去吗?)。