最近有人问我这个问题–在Java中使用+运算符连接字符串是否对性能不利?

这让我开始思考Java中连接字符串的不同方法,以及它们如何相互对抗。 这些是我要研究的方法:

  1. 使用+运算符
  2. 使用StringBuilder
  3. 使用StringBuffer
  4. 使用String.concat()
  5. 使用String.joinString.join新增功能)

我也尝试了String.format()但是那太慢了,以至于我暂时不在本文中介绍。

在继续之前,我们应该分离两个用例:

  1. 将两个字符串作为单个调用连接在一起,例如在日志消息中。 因为这只是一个电话,您可能会认为性能几乎不是问题,但结果仍然很有趣,并且可以阐明该主题。
  2. 在一个循环中连接两个字符串。 性能尤其是一个问题,尤其是在循环较大的情况下。

我最初的想法和问题如下:

  1. +运算符是用StringBuilder实现的,因此至少在连接两个String的情况下,它应产生与StringBuilder类似的结果。 幕后到底发生了什么?
  2. 在所有类都是出于连接字符串并取代StringBuffer的目的而设计的之后,StringBuilder应该是最有效的方法。 但是,与String.concat()相比,创建StringBuilder的开销是多少?
  3. StringBuffer是用于连接字符串的原始类–不幸的是,其方法是同步的。 确实不需要同步,并且随后将其替换为未同步的StringBuilder。 问题是,JIT是否优化了同步?
  4. String.concat()应该适用于2个字符串,但是在循环中是否可以正常工作?
  5. String.join()具有比StringBuilder更多的功能,如果我们指示它使用空的定界符来联接String,它将如何影响性能?

我要解决的第一个问题是+运算符的工作方式。 我一直都知道它在幕后使用了StringBuilder,但是要证明这一点,我们需要检查字节码。

如今 ,查看字节码最简单的方法是使用JITWatch ,这是一个非常出色的工具,旨在了解JIT如何编译您的代码。 它有一个很棒的视图,您可以在其中与字节码(如果要进入该级别,还可以是机器码)并排查看源代码。

这是一个非常简单的方法plus2()的字节码,我们可以看到确实在第6行上创建了一个StringBuilder并附加了变量a(第14行)和b(第18行)。

我认为将其与StringBuffer的手工使用进行比较会很有趣,因此我创建了另一个方法build2(),结果如下。

此处生成的字节码不如plus()方法那么紧凑。 StringBuilder存储在变量高速缓存中(第13行),而不是仅留在堆栈上。 我不知道为什么会这样,但是JIT可能能够做到这一点,我们将不得不看看时机如何。

无论如何,如果用plus运算符和StringBuilder将2个字符串连接起来的结果显着不同,那将是非常令人惊讶的。

我写了一个小型的JMH测试来确定不同方法的执行方式。 让我们首先看一下两个Strings测试。 参见下面的代码:

package org.sample;import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;import java.util.UUID;
import java.util.concurrent.TimeUnit;@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@State(Scope.Thread)
public class LoopStringsBenchmark {private String[] strings;@Setuppublic void setupTest(){strings = new String[100];for(int i = 0; i<100; i++) {strings[i] = UUID.randomUUID().toString().substring(0, 10);}}@Benchmarkpublic void testPlus(Blackhole bh) {String combined = "";for(String s : strings) {combined = combined + s;}bh.consume(combined);}@Benchmarkpublic void testStringBuilder(Blackhole bh) {StringBuilder sb = new StringBuilder();for(String s : strings) {sb.append(s);}bh.consume(sb.toString());}@Benchmarkpublic void testStringBuffer(Blackhole bh) {StringBuffer sb = new StringBuffer();for(String s : strings) {sb.append(s);}bh.consume(sb.toString());}@Benchmarkpublic void testStringJoiner(Blackhole bh) {bh.consume(String.join("", strings));}@Benchmarkpublic void testStringConcat(Blackhole bh) {String combined = "";for(String s : strings) {combined.concat(s);}bh.consume(combined);}
}

结果看起来像这样:

显而易见的赢家是String.concat()。 毫不奇怪,因为它不必为每次调用创建StringBuilder / StringBuffer而付出性能损失。 虽然确实需要每次都创建一个新的String(这将在以后变得很重要),但是对于连接两个Sting的非常简单的情况,它更快。

另一点是,尽管产生了额外的字节码,但正如我们预期的那样,plus和StringBuilder是等效的。 StringBuffer仅比StringBuilder慢一点,这很有趣,这表明JIT必须在做一些魔术来优化同步。

下一个测试将创建一个由100个字符串组成的数组,每个字符串包含10个字符。 基准比较了将100个字符串连接在一起的不同方法所花费的时间。 参见下面的代码:

这次的结果看起来完全不同:

在这里,加号方法确实遭受了损失。 每次循环时创建StringBuilder的开销都非常大。 您可以在字节码中清楚地看到这一点:

您可以看到每次执行循环时都会创建一个新的StringBuilder(第30行)。 可以争论的是,JIT应该发现这一点并能够对其进行优化,但是事实并非如此,并且使用+变得非常慢。

同样,StringBuilder和StringBuffer的性能完全相同,但是这次它们都比String.concat()快。 String.concat()为在循环的每次迭代中创建新的String付出的代价最终会增加,并且StringBuilder变得更有效率。

给定可以添加到此方法的所有其他功能,String.join()效果很好,但是,正如预期的那样,对于纯串联而言,它不是最佳选择。

摘要

如果要在单行代码中连接字符串,我将使用+运算符,因为它最易读,并且性能对于单次调用实际上并不重要。 还要提防String.concat(),因为您几乎肯定会需要执行空检查 ,而其他方法则不需要这样做。

在循环中串联字符串时,应使用StringBuilder。 您可以使用StringBuffer,但我不一定在所有情况下都信任JIT来像基准测试中那样高效地优化同步。

我的所有结果都是使用JMH取得的,并且都带有通常的健康警告 。

翻译自: https://www.javacodegeeks.com/2015/02/optimum-method-concatenate-strings-java.html

Java中连接字符串的最佳方法相关推荐

  1. java字符连接字符串数组_Java中连接字符串的最佳方法

    java字符连接字符串数组 最近有人问我这个问题–在Java中使用+运算符连接字符串是否对性能不利? 这让我开始思考Java中连接字符串的不同方法,以及它们如何相互对抗. 这些是我要研究的方法: 使用 ...

  2. 在Python中连接字符串的首选方法是什么?

    本文翻译自:Which is the preferred way to concatenate a string in Python? Since Python's string can't be c ...

  3. 【转】在Java中连接字符串时是使用+号还是使用StringBuilder StringBuffer 加号

    字符串是Java程序中最常用的一种数据结构之一.在Java中的String类已经重载的"+".也就是说,字符串可以直接使用"+"进行连接,如下面代码所示: St ...

  4. 在Java中连接字符串时是使用+号还是使用StringBuilder

    字符串是Java程序中最常用的一种数据结构之一.在Java中的String类已经重载的"+".也就是说,字符串可以直接使用"+"进行连接,如下面代码所示: St ...

  5. java输入一串字符串反转_反转Java中的字符串

    java输入一串字符串反转 Reverse a String in java is a good coding related interview question. I have seen inte ...

  6. Java中String类的concat方法___java的String字符串的concat()方法连接字符串和“+“连接字符串解释

    Java中String类的concat方法 在了解concat()之前,首先需要明确的是String的两点特殊性. 长度不可变 值不可变 这两点从源码中对String的声明可以体现: private ...

  7. linux shell脚本字符串连接符,学习Linux shell脚本中连接字符串的方法

    这篇文章主要介绍了Linux shell脚本中连接字符串的方法,如果想要在变量后面添加一个字符,可以用一下方法: 代码如下: $value1=home $value2=${value1}"= ...

  8. java分隔符 字符串_用Java构建带分隔符的字符串的最佳方法是什么?

    在Java应用程序中工作时,最近我需要组合一个用逗号分隔的值列表,以传递给另一个Web服务,而无需事先知道会有多少个元素.我能想到的最好的办法是这样的: public String appendWit ...

  9. java oracle 连接字符串函数_通过shell来比较oracle和java中的字符串使用

    这些准备工作齐了之后,我们来从Java中的字符串使用入手来比较一下oracle中对于字符串的处理. java中有如下的一些函数,我会依次来做比较. public char charAt(int ind ...

最新文章

  1. python wsdl connection refused 111
  2. 机器视觉特征提取:HOG、SIFT、SURF、ORB、LBP、HAAR
  3. java中对集合排序,Java如何对集合中的项目排序?
  4. 计算机网络 之 TCP和UDP的端口号解析
  5. iOS Assigning to 'idXXXDelegate' from incompatible type 'BViewController *__strong'
  6. UVaLive 7361(矩阵快速幂)
  7. mysql数据库ip地址_MySQL数据库存储IP地址的方法
  8. excel表格不够怎么添加_这个Excel表格,怎么做的这么漂亮
  9. tensorflow版本升级后的各种方法问题
  10. 开源贡献 计算_公司如何帮助员工为开源做出贡献
  11. class构造函数实现接口数据整合
  12. Atitit mysql存储过程编写指南 1. 定义变量 1 1.1. 变量名以@开头用户变量 会话变量 1 1.2. 以declare关键字声明 存储过程变量 2 1.3. @是用户自定义变量,
  13. 新版标准日本语初级_第二课
  14. 安装与部署Exchange server 2013
  15. 教你把mov格式的视频转换mp4
  16. java 包扫描 —————— 开开开山怪
  17. 远程桌面协助的计算机名是什么意思,windows远程桌面和远程协助有什么区别
  18. (Pytorch) YOLOV4 : 训练自己的数据集【左侧有码】
  19. 为什么你需要辞职了?——辞职的十大理由
  20. 新生儿肚脐清洗小窍门

热门文章

  1. ssh(Spring+Spring mvc+hibernate)——EmpDaoImpl.java
  2. 消费端整合SpringCloudGateway
  3. java电商面试介绍
  4. centos7离线安装oracle11g,CentOS 7.5离线安装Oracle 11gR2
  5. 数据结构树的基本操作_树的各类基本操作(数据结构)
  6. eclipse如何设置js源文件编码
  7. 关于Object.clone克隆方法的测试
  8. 网络——Base64Encode(转:自定义Base64编码器——Base64Encode)
  9. JAVA实现一个图片上传预览功能
  10. 快速排序算法思想及实现