关于 Stringbuilder 中 append 方法的实现有一个疑问

2022-06-14 12:19:13 +08:00
 zhao1014
public AbstractStringBuilder append(String str) {
    // 如果 str 为 null ,则在字符数组中添加 'n''u''l''l'
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }```
    

字符串参数为 null 时调用了 appendNull(); 方法
```java
    private AbstractStringBuilder appendNull() {
        int c = count;
        ensureCapacityInternal(c + 4);
        // 这里将内部字符数组赋值给局部变量
        final char[] value = this.value;
        // 然后通过局部变量向内部数组添加字符
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        // 这里的 count 也是先赋值给局部变量,为什么不直接使用 count 呢?
        count = c;
        return this;
    }```
    
我的疑问是 appendNull 方法中为什么要创建一个 final char[] value 这样一个局部变量来操作内部数组而不是直接使用 this.value ?这么做的用意是什么?
2017 次点击
所在节点    Java
18 条回复
GuuJiang
2022-06-14 12:22:12 +08:00
不这样的话下面的每一行代码都要写成 this.value[c++],对应的 opcode 要多一次 get_field 操作
chendy
2022-06-14 12:23:29 +08:00
压榨性能
可以省下后续用 this 找 value 的开销
zhao1014
2022-06-14 12:51:58 +08:00
还是有点不理解,写成这样的话也不用 this 调用了啊
```java
private AbstractStringBuilder appendNull() {

ensureCapacityInternal(c + 4);

value[count++] = 'n';
value[count++] = 'u';
value[count++] = 'l';
value[count++] = 'l';

return this;
}
zhao1014
2022-06-14 12:53:14 +08:00
使用实例变量调用跟使用局部变量调用的区别在哪呢?不太明白
zhao1014
2022-06-14 12:54:50 +08:00
除了多线程的问题以外想不到区别了:(
TWorldIsNButThis
2022-06-14 13:30:10 +08:00
直接找 count 是在堆上找
你在栈上声明一遍下面用的地方就是从栈上直接拿
mxalbert1996
2022-06-14 13:40:11 +08:00
@zhao1014
使用局部变量比取得 4 次成员变量要更高效。
在这里 `value` 和 `this.value` 完全等价,字节码也不会有任何区别。
skinny
2022-06-14 13:42:04 +08:00
可以少打几十个字符而已,JVM 不至于这么蠢这么简单的代码都不会优化。
kiroter
2022-06-14 13:43:04 +08:00
习惯问题,查看引用的时候会少很多。get 什么编译器应该会优化的
liyunyang
2022-06-14 13:53:28 +08:00
final char[] value 定义后在堆上会有固定的 value 指向(无法修改引用地址)
下次再进来的时候可以直接用
maokabc
2022-06-14 14:00:43 +08:00
用 javap 看字节码就可以发现区别,就是 gefield 指令比 aload 、iload 这类指令开销大,更别说 getfield 之前还有一条 aload_0 指令先得到 this 。
cubecube
2022-06-14 17:02:08 +08:00
@skinny 字节码层面,真不会优化。。别把 jvm 想得太高级。jit 层面未来没准会吧
huyangq
2022-06-14 17:09:46 +08:00
11 楼正解
GuuJiang
2022-06-14 17:32:39 +08:00
@skinny 这里是不可能自动优化的,因为无法确保 this.value 不会被别的线程修改
aguesuka
2022-06-15 11:20:30 +08:00
你们要笑死我了, 就是个代码风格的问题, 来换个 jdk, 就不一样了.
AbstractStringBuilder 完全不 care 性能, 因为它不是 public 的, 两个实现 StringBuilder/StringBuffer 上面有 IntrinsicCandidate 的注解. The @IntrinsicCandidate annotation is specific to the HotSpot Virtual Machine.



private AbstractStringBuilder appendNull() {
ensureCapacityInternal(count + 4);
int count = this.count;
byte[] val = this.value;
if (isLatin1()) {
val[count++] = 'n';
val[count++] = 'u';
val[count++] = 'l';
val[count++] = 'l';
} else {
count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l');
}
this.count = count;
return this;
}
ThreeK
2022-06-22 17:43:08 +08:00
@aguesuka 你这代码不 copy 了个寂寞吗,不还是用了局部变量。
aguesuka
2022-06-22 20:42:45 +08:00
@ThreeK 如果效率有区别, 那么方法体里第一行应该和第二行互换, 但是没有, 说明没有区别
ThreeK
2022-06-23 09:55:59 +08:00
@aguesuka 这样啊,理解了,是我关注点不一样,光盯着 value 的数组了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/859504

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX