Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
sweetning0809 opened this issue Aug 3, 2024 · 5 comments
Closed

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

sweetning0809 opened this issue Aug 3, 2024 · 5 comments

Comments

@sweetning0809
Copy link
Contributor

sweetning0809 commented Aug 3, 2024

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
@sweetning0809
Copy link
Contributor Author

sweetning0809 commented Aug 3, 2024

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

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

@sweetning0809
Copy link
Contributor Author

sweetning0809 commented Aug 3, 2024

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

@sweetning0809
Copy link
Contributor Author

sweetning0809 commented Aug 3, 2024

补充实验

使用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

@Snailclimb
Copy link
Owner

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

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

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

@sweetning0809
Copy link
Contributor Author

我觉得这一部分描述存在歧义,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
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants