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

LeetCode 中使用 StringBuilder 连接字符串为什么会比用+号连接快?

  •  
  •   kuretru ·
    kuretru · 2021-01-10 11:05:18 +08:00 · 3129 次点击
    这是一个创建于 1438 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今日 LeetCode 的每日一题,代码很简单,官方的题解如下:

    class Solution {
        public List<String> summaryRanges(int[] nums) {
            List<String> ret = new ArrayList<String>();
            int i = 0;
            int n = nums.length;
            while (i < n) {
                int low = i;
                i++;
                while (i < n && nums[i] == nums[i - 1] + 1) {
                    i++;
                }
                int high = i - 1;
                StringBuffer temp = new StringBuffer(Integer.toString(nums[low]));
                if (low < high) {
                    temp.append("->");
                    temp.append(Integer.toString(nums[high]));
                }
                ret.add(temp.toString());
            }
            return ret;
        }
    }
    

    可以看到官方使用 StringBuffer 来连接字符串,在不需要考虑线程安全的环境,毫无疑问可以使用 StringBuilder 来代替,但是问题在于,为什么使用+号来连接的效果会比 StringBuilder 及 StringBuffer 差?

    String temp = Integer.toString(nums[low]);
    if (low < high) {
    temp += "->" + Integer.toString(nums[high]);
    }
    ret.add(temp);
    

    两者的差别为:
    StringBuilder/StringBuffer:执行用时:0 ms, 在所有 Java 提交中击败了 100.00%的用户
    +号连接:执行用时:8 ms, 在所有 Java 提交中击败了 70.02%的用户
    同样的代码在本地各循环 1000 万次的结果为
    JDK 1.8(与 LeetCode 相同):7708ms vs 7287ms
    JDK 11:7320ms vs 4511ms

    9 条回复    2021-01-10 15:00:38 +08:00
    snw
        1
    snw  
       2021-01-10 11:23:24 +08:00
    我记得大致上是因为用 + 时,内部会创建一个新的 string,然后把连接的值赋给这个新的 string 。
    用 StringBuilder 的话是直接把增加的字符 append 到原字符串后面,省去了创建新 string 、删除旧 string 的消耗。
    kuretru
        2
    kuretru  
    OP
       2021-01-10 11:26:16 +08:00
    @snw #1
    你说的是循环内的情况把
    ```java
    String text = "";
    for(){
    test += something;
    }
    ```
    这种情况下,使用 StringBuilder 和 StringBuffer 无疑
    vincentxue
        3
    vincentxue  
       2021-01-10 11:57:31 +08:00
    这个和循环没关系,循环只是进一步放大了性能问题,字符串只要是拼接变量,不管放在哪里,编译器都会给你换成 StringBuilder 的。最直接的办法就是把你两份代码转成字节码一对比你就能看明白里面的原因。
    Merlini
        4
    Merlini  
       2021-01-10 12:08:49 +08:00
    正好最近在入门 java

    > 有些时候 , 需要由较短的字符串构建字符串 , 例如 , 按键或来自文件中的单词。 采用字符串连接的方式达到此目的效率比较低。 每次连接字符串 ,都会构建一个新的 String 对象 ,既耗时 , 又浪费空间。 使用 StringBuilder 类就可以避免这个问题的发生。
    取自 java 核心卷 1
    micean
        5
    micean  
       2021-01-10 12:09:13 +08:00 via Android
    这是 java 的基础问题啊……加号在不可优化的情况下每使用一次相当于拷贝了一次旧串 byte[]和新增串 byte[],性能当然就低了
    AllenHua
        6
    AllenHua  
       2021-01-10 12:13:14 +08:00
    如果只是简单的字符串拼接 使用 concat 比 使用 + 效率更高

    但是 concat 只能 concat String 类型的数据

    + 可以 拼接其他基础类型的数据

    老生常谈 如果大量操作 String 务必使用封装类 StringBuffer 或 StringBuilder 或 StringJoiner
    hoyixi
        7
    hoyixi  
       2021-01-10 12:25:24 +08:00
    这是 N 年前面试官爱问的问题,已经被问烂了
    Jooooooooo
        8
    Jooooooooo  
       2021-01-10 14:49:08 +08:00
    String 的 + 每次都 new 对象

    非常大的循环记得用 StringBuilder
    340244120w
        9
    340244120w  
       2021-01-10 15:00:38 +08:00 via iPhone
    楼上大部分都看的 java 核心思想吧。其实现在 8 之后,循环里直接拼字符串,相当于每次循环都创建了一个 stringbuilder,相对于循环外面只创建一个,自然就慢了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4088 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 04:10 · PVG 12:10 · LAX 20:10 · JFK 23:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.