我查看了String.hashcode()方法的源代码。这是6-b14
中的实施,已经改变了。
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
我的问题是关于这一行:
int len = count;
其中count
是表示字符串字符数的全局变量。
为什么局部变量len
在这里用于循环条件而不是全局变量本身?因为没有操纵变量,只能阅读。如果使用全局字段来读取或写入本地变量,那么使用局部变量是一种好习惯吗?如果答案是肯定的,为什么还要阅读?
答案 0 :(得分:2)
访问本地变量比实例变量快。此外,新的Java 8 Code(请参阅Anubians的答案)也考虑到了这一点。这就是为什么他们使用局部变量h
进行哈希计算而不直接访问实例变量this.hash
并创建本地指针char val[] = value;
的原因。但考虑到这一点,我不知道他们为什么不在{for循环中使用i < val.length;
甚至更好z = val.length; i < z;
但i < value.length;
。
答案 1 :(得分:2)
在String类中找到了一个关于String.trim()方法读取"avoid getfield opcode"
的局部变量奇怪赋值的注释。
public String trim() {
int len = value.length;
int st = 0;
char[] val = value; /* avoid getfield opcode */
while ((st < len) && (val[st] <= ' ')) {
st++;
}
while ((st < len) && (val[len - 1] <= ' ')) {
len--;
}
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
所以整个事情似乎与性能有关,正如Frank Olschewski指出的那样。
在Java字节码中,实例变量实际上由对象和名称引用(使用GETFIELD
指令)。如果没有优化,VM必须做更多工作才能访问变量。
然后,代码的潜在性能损失是它在每次循环中使用相对昂贵的GETFIELD
指令。该方法中的局部赋值在每次循环时都不需要GETFIELD
。
JIT优化器可能会优化循环,但也可能没有,因此开发人员可能会采取安全路径手动强制执行。
Avoiding getfield opcode上有一个单独的问题,其中包含详细信息。
答案 2 :(得分:1)
如果可以修改count
,那么您需要一个局部变量。如果你正在进行多线程处理,那么你需要一个局部变量。创建局部变量是最安全的。但是,这并不是严格的必要条件。
在这种情况下,它太过分了,因为Strings无论如何都是不可变的。伯爵的价值甚至无法改变。
这几乎没用,这就是为什么在Java 8中它看起来像这样:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
他们甚至不再计算,他们正在使用value.length
,其中value
是最终的char数组。
他们正在做char val[] = value
,但这只是一个参考,绝对是不必要的。
通过使用局部变量可能会有一些微妙的微增强,或者它可能是为了可读性而做的,但它不是必需的(在我看来不太可读)。