V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhao1014
V2EX  ›  Java

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

  •  
  •   zhao1014 · 2022-06-14 12:19:13 +08:00 · 2243 次点击
    这是一个创建于 930 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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 ?这么做的用意是什么?
    18 条回复    2022-06-23 09:55:59 +08:00
    GuuJiang
        1
    GuuJiang  
       2022-06-14 12:22:12 +08:00 via iPhone   ❤️ 1
    不这样的话下面的每一行代码都要写成 this.value[c++],对应的 opcode 要多一次 get_field 操作
    chendy
        2
    chendy  
       2022-06-14 12:23:29 +08:00   ❤️ 1
    压榨性能
    可以省下后续用 this 找 value 的开销
    zhao1014
        3
    zhao1014  
    OP
       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
        4
    zhao1014  
    OP
       2022-06-14 12:53:14 +08:00
    使用实例变量调用跟使用局部变量调用的区别在哪呢?不太明白
    zhao1014
        5
    zhao1014  
    OP
       2022-06-14 12:54:50 +08:00
    除了多线程的问题以外想不到区别了:(
    TWorldIsNButThis
        6
    TWorldIsNButThis  
       2022-06-14 13:30:10 +08:00 via iPhone   ❤️ 1
    直接找 count 是在堆上找
    你在栈上声明一遍下面用的地方就是从栈上直接拿
    mxalbert1996
        7
    mxalbert1996  
       2022-06-14 13:40:11 +08:00 via Android   ❤️ 1
    @zhao1014
    使用局部变量比取得 4 次成员变量要更高效。
    在这里 `value` 和 `this.value` 完全等价,字节码也不会有任何区别。
    skinny
        8
    skinny  
       2022-06-14 13:42:04 +08:00
    可以少打几十个字符而已,JVM 不至于这么蠢这么简单的代码都不会优化。
    kiroter
        9
    kiroter  
       2022-06-14 13:43:04 +08:00
    习惯问题,查看引用的时候会少很多。get 什么编译器应该会优化的
    liyunyang
        10
    liyunyang  
       2022-06-14 13:53:28 +08:00   ❤️ 1
    final char[] value 定义后在堆上会有固定的 value 指向(无法修改引用地址)
    下次再进来的时候可以直接用
    maokabc
        11
    maokabc  
       2022-06-14 14:00:43 +08:00 via Android   ❤️ 2
    用 javap 看字节码就可以发现区别,就是 gefield 指令比 aload 、iload 这类指令开销大,更别说 getfield 之前还有一条 aload_0 指令先得到 this 。
    cubecube
        12
    cubecube  
       2022-06-14 17:02:08 +08:00
    @skinny 字节码层面,真不会优化。。别把 jvm 想得太高级。jit 层面未来没准会吧
    huyangq
        13
    huyangq  
       2022-06-14 17:09:46 +08:00
    11 楼正解
    GuuJiang
        14
    GuuJiang  
       2022-06-14 17:32:39 +08:00 via iPhone
    @skinny 这里是不可能自动优化的,因为无法确保 this.value 不会被别的线程修改
    aguesuka
        15
    aguesuka  
       2022-06-15 11:20:30 +08:00   ❤️ 1
    你们要笑死我了, 就是个代码风格的问题, 来换个 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
        16
    ThreeK  
       2022-06-22 17:43:08 +08:00
    @aguesuka 你这代码不 copy 了个寂寞吗,不还是用了局部变量。
    aguesuka
        17
    aguesuka  
       2022-06-22 20:42:45 +08:00
    @ThreeK 如果效率有区别, 那么方法体里第一行应该和第二行互换, 但是没有, 说明没有区别
    ThreeK
        18
    ThreeK  
       2022-06-23 09:55:59 +08:00
    @aguesuka 这样啊,理解了,是我关注点不一样,光盯着 value 的数组了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1098 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 18:56 · PVG 02:56 · LAX 10:56 · JFK 13:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.