马上加入IBC程序猿 各种源码随意下,各种教程随便看! 注册 每日签到 加入编程讨论群

C#教程 ASP.NET教程 C#视频教程程序源码享受不尽 C#技术求助 ASP.NET技术求助

【源码下载】 社群合作 申请版主 程序开发 【远程协助】 每天乐一乐 每日签到 【承接外包项目】 面试-葵花宝典下载

官方一群:

官方二群:

为什么阿里巴巴Java开发手册中不建议在循环体中使用+进行字符串拼接?

[复制链接]
查看1953 | 回复0 | 2019-10-24 09:47:44 | 显示全部楼层 |阅读模式

之前在阅读《阿里巴巴Java开发手册》时,发现有一条是关于循环体中字符串拼接的建议,具体内容如下:

094744fdqd3hek3nx7nuk5.png

那么我们起首来用例子来看看在循环体中用 + 或者用 StringBuilder 举行字符串拼接的服从怎样吧(JDK版本为 jdk1.8.0_201)。

  1. <code>package com.wupx.demo;
  2. /**
  3. * @author wupx
  4. * @date 2019/10/23
  5. */
  6. public class StringConcatDemo {
  7. public static void main(String[] args) {
  8. long s1 = System.currentTimeMillis();
  9. new StringConcatDemo().addMethod();
  10. System.out.println("利用 + 拼接:" + (System.currentTimeMillis() - s1));
  11. s1 = System.currentTimeMillis();
  12. new StringConcatDemo().stringBuilderMethod();
  13. System.out.println("利用 StringBuilder 拼接:" + (System.currentTimeMillis() - s1));
  14. }
  15. public String addMethod() {
  16. String result = "";
  17. for (int i = 0; i < 100000; i++) {
  18. result += (i + "武培轩");
  19. }
  20. return result;
  21. }
  22. public String stringBuilderMethod() {
  23. StringBuilder result = new StringBuilder();
  24. for (int i = 0; i < 100000; i++) {
  25. result.append(i).append("武培轩");
  26. }
  27. return result.toString();
  28. }
  29. }</code>
复制代码

实行效果如下:

  1. <code>利用 + 拼接:29282
  2. 利用 StringBuilder 拼接:4</code>
复制代码

为什么这两种方法的时间会差这么多呢?接下来让我们一起进一步研究。

为什么 StringBuilder 比 + 快这么多?

从字节码层面来看下,为什么循环体中字符串拼接 StringBuilder 比 + 快这么多?

利用 javac StringConcatDemo.java 下令编译源文件,利用 javap -c StringConcatDemo 下令检察字节码文件的内容。

此中 addMethod() 方法的字节码如下:

  1. <code> public java.lang.String addMethod();
  2. Code:
  3. 0: ldc #16 // String
  4. 2: astore_1
  5. 3: iconst_0
  6. 4: istore_2
  7. 5: iload_2
  8. 6: ldc #17 // int 100000
  9. 8: if_icmpge 41
  10. 11: new #7 // class java/lang/StringBuilder
  11. 14: dup
  12. 15: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
  13. 18: aload_1
  14. 19: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  15. 22: iload_2
  16. 23: invokevirtual #18 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  17. 26: ldc #19 // String wupx
  18. 28: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  19. 31: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  20. 34: astore_1
  21. 35: iinc 2, 1
  22. 38: goto 5
  23. 41: aload_1
  24. 42: areturn</code>
复制代码

可以看出,第 8 行到第 38 行构成了一个循环体:在第 8 行的时间做条件判定,如果不满意循环条件,则跳转到 41 行。编译器做了一定水平的优化,在 11 行 new 了一个 StringBuilder 对象,然后再 19 行、23 行、28 行举行了三次 append() 方法的调用,不外每次循环都会重新 new 一个 StringBuilder 对象。

再来看 stringBuilderMethod() 方法的字节码:

  1. <code> public java.lang.String stringBuilderMethod();
  2. Code:
  3. 0: new #7 // class java/lang/StringBuilder
  4. 3: dup
  5. 4: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
  6. 7: astore_1
  7. 8: iconst_0
  8. 9: istore_2
  9. 10: iload_2
  10. 11: ldc #17 // int 100000
  11. 13: if_icmpge 33
  12. 16: aload_1
  13. 17: iload_2
  14. 18: invokevirtual #18 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  15. 21: ldc #19 // String wupx
  16. 23: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  17. 26: pop
  18. 27: iinc 2, 1
  19. 30: goto 10
  20. 33: aload_1
  21. 34: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  22. 37: areturn</code>
复制代码

13 行到 30 行构成了循环体,可以看出,在第4行(循环体外)就构建好了 StringBuilder 对象,然后再循环体内只举行 append() 方法的调用。

由此可以看出,在 for 循环中,利用 + 举行字符串拼接,每次都是 new 了一个 StringBuilder,然后再把 String 转成 StringBuilder,再举行 append,而频仍的新建对象不仅要泯灭许多时间,还会造成内存资源的浪费。这就从字节码层面表明了为什么不建议在循环体内利用 + 去举行字符串的拼接。

接下来再来让我们看下利用 + 或者 StringBuilder 拼接字符串的原理吧。

利用 + 拼接字符串

在 Java 开发中,最简朴常用的字符串拼接方法就是直接利用 + 来完成:

  1. <code>String boy = "wupx";
  2. String girl = "huyx";
  3. String love = boy + girl;</code>
复制代码

反编译后的内容如下:(利用的反编译工具为 jad)

  1. <code>String boy = "wupx";
  2. String girl = "huyx";
  3. String love = (new StringBuilder()).append(boy).append(girl).toString();</code>
复制代码

通过检察反编译以后的代码,可以发现,在字符串常量在拼接过程中,是将 String 转成了 StringBuilder 后,利用其 append() 方法举行处理的。

那么也就是说,Java中的 + 对字符串的拼接,其实现原理是利用 StringBuilder 的 append() 来实现的,利用 + 拼接字符串,其实只是 Java 提供的一个语法糖。

利用 StringBuilder 拼接字符串

StringBuilder 的 append 方法就是第二个常用的字符串拼接姿势了。

和 String 类雷同,StringBuilder 类也封装了一个字符数组,定义如下:

  1. <code>char[] value;</code>
复制代码

与 String 不同的是,它并不是 final 的,所以是可以修改的。另外,与 String 不同,字符数组中不一定全部位置都已经被利用,它有一个实例变量,表现数组中已经利用的字符个数,定义如下:

  1. <code>int count;</code>
复制代码

其 append() 方法源码如下:

  1. <code>public StringBuilder append(String str) {
  2. super.append(str);
  3. return this;
  4. }</code>
复制代码

该类继承了 AbstractStringBuilder 类,看下其 append() 方法:

  1. <code>public AbstractStringBuilder append(String str) {
  2. if (str == null)
  3. return appendNull();
  4. int len = str.length();
  5. ensureCapacityInternal(count + len);
  6. str.getChars(0, len, value, count);
  7. count += len;
  8. return this;
  9. }</code>
复制代码

起首判定拼接的字符串 str 是不是 null,如果是,调用 appendNull() 方法举行处理,appendNull() 方法的源码如下:

  1. <code>private AbstractStringBuilder appendNull() {
  2. int c = count;
  3. ensureCapacityInternal(c + 4);
  4. final char[] value = this.value;
  5. value[c++] = &#39;n&#39;;
  6. value[c++] = &#39;u&#39;;
  7. value[c++] = &#39;l&#39;;
  8. value[c++] = &#39;l&#39;;
  9. count = c;
  10. return this;
  11. }</code>
复制代码

如果字符串 str 不为 null,则判定拼接后的字符数组长度是否超过当前数组长度,如果超过,则调用 Arrays.copyOf() 方法举行扩容并复制,ensureCapacityInternal() 方法的源码如下:

  1. <code>private void ensureCapacityInternal(int minimumCapacity) {
  2. if (minimumCapacity - value.length > 0) {
  3. value = Arrays.copyOf(value,
  4. newCapacity(minimumCapacity));
  5. }
  6. }</code>
复制代码

末了,将拼接的字符串 str 复制到目的数组 value 中。

  1. <code>str.getChars(0, len, value, count);</code>
复制代码

总结

本文针对《阿里巴巴Java开发手册》中的循环体中拼接字符串建议出发,从字节码层面,来表明为什么 StringBuilder 比 + 快,还分别介绍了字符串拼接中 + 和 StringBuilder 的原理,因此在循环体拼接字符串时,应该利用 StringBuilder 的 append() 去完成拼接。







来源:https://www.cnblogs.com/wupeixuan/p/11729920.html
C#论坛 www.ibcibc.com IBC编程社区
C#
C#论坛
IBC编程社区
*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则