Skip to content

关于字符串拼接及优化的疑问 #2442

Closed

Description

@sweetning0809
image

我觉得这一部分描述存在歧义,java9优化中解决的是A+B+C的字符串拼接使用的相当于提前分配空间,但实际上对于循环的拼接依旧是两个两个动态分配内存,其实和两个两个append是一个概念(因为两个拼接不会出现额外需要扩容的情况)?肯定不如自己去string builder 然后append。
以下字节码使用的java17编译的:

    public static void main(String[] args) {
        String[] arr = new String[]{"he", "llo", "world", "!"};
        String s = "";

        for(int i = 0; i < arr.length; ++i) {
            s = s + arr[i];
        }

        System.out.println(s);
    }
image

Activity

  1. sweetning0809 commented on Aug 3, 2024

    @sweetning0809
    ContributorAuthor

    先随便进行了一点简单的测试,时间确实有很明显落后,可以拿工具再进一步测试时间和内存。

    class test{
        public static void main(String[] args) {
            int[] intarr = java.util.stream.IntStream.rangeClosed(1, 100000).toArray();
            String[] arr = Arrays.stream(intarr).mapToObj(String::valueOf).toArray(String[]::new);
            System.out.println("1");
            long startTime = System.nanoTime();
            String s = "";
            for (int i = 0; i < arr.length; i++) {
                s += arr[i];
            }
            long endTime = System.nanoTime();
            // 计算运行时间
            long duration = endTime - startTime;
            // 输出运行时间,转换为毫秒
            System.out.println("运行时间: " + duration / 1000000 + "毫秒");
    
    
    
            startTime = System.nanoTime();
            StringBuilder sb = new StringBuilder();
            for (String str : arr) {
                sb.append(str);
            }
            endTime = System.nanoTime();
            // 计算运行时间
            duration = endTime - startTime;
            // 输出运行时间,转换为毫秒
            System.out.println("运行时间: " + duration / 1000000 + "毫秒");
        }
    }
    
    image
  2. sweetning0809 commented on Aug 3, 2024

    @sweetning0809
    ContributorAuthor

    我觉得创建大量临时对象的问题并没有得到解决,只是使用动态创建替换了原来的每一个创建buillder,但是每次创建的的问题并没有得到缓解。
    image
    每次依旧是两位的拼接。

  3. sweetning0809 commented on Aug 3, 2024

    @sweetning0809
    ContributorAuthor

    补充实验

    使用JMH进一步实验,在循环拼接只有2的情况下就有了很明显的落后,更不要说循环次数较多情况(几乎是线性关系的时间增加),所以在循环拼接的时候仍然应该使用string builder。

    循环拼接:

    Benchmark Mode Cnt Score Error Units
    TeststringApplicationTests.testConcatLatinFor thrpt 25 14461326.741 ± 73113.873 ops/s

    构造string builder append拼接

    Benchmark Mode Cnt Score Error Units
    TeststringApplicationTests.testConcatWithSameBuilder thrpt 25 16331234.883 ± 1184275.498 ops/s

  4. Snailclimb commented on Aug 4, 2024

    @Snailclimb
    Owner

    我觉得这一部分描述存在歧义,java9优化中解决的是A+B+C的字符串拼接使用的相当于提前分配空间,但实际上对于循环的拼接依旧是两个两个动态分配内存,其实和两个两个append是一个概念(因为两个拼接不会出现额外需要扩容的情况)?肯定不如自己去string builder 然后append。

    感谢分享,这样总结会不会更合适一些呢?

    在 JDK 9 中,字符串相加“+”改为用动态方法 makeConcatWithConstants() 来实现,从而减少了部分临时对象的创建。然而,这种优化主要针对简单的字符串拼接,对于循环中的大量拼接操作,仍然会逐个动态分配内存(类似于两个两个 append 的概念),并不如手动使用 StringBuilder 来进行拼接效率高。

  5. sweetning0809 commented on Aug 4, 2024

    @sweetning0809
    ContributorAuthor

    我觉得这一部分描述存在歧义,java9优化中解决的是A+B+C的字符串拼接使用的相当于提前分配空间,但实际上对于循环的拼接依旧是两个两个动态分配内存,其实和两个两个append是一个概念(因为两个拼接不会出现额外需要扩容的情况)?肯定不如自己去string builder 然后append。

    感谢分享,这样总结会不会更合适一些呢?

    在 JDK 9 中,字符串相加“+”改为用动态方法 makeConcatWithConstants() 来实现,从而减少了部分临时对象的创建。然而,这种优化主要针对简单的字符串拼接,对于循环中的大量拼接操作,仍然会逐个动态分配内存(类似于两个两个 append 的概念),并不如手动使用 StringBuilder 来进行拼接效率高。

    嗯 对的 我认为是这样的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions