在Java中对字符串的操作可以说是最常用的,在对字符串的操作中有三种拼接字符串的方法,下面我们来看看这三种方式有何不同,在什么时候用比较合适。

一、从耗时角度看

先来看一段代码:

package com.codeing.snail.test;

public class StringFormat {
private static long startPointTime;
public static void main(String[] args) {
String s1 = "小明";
String s2 = "和";
String s3 = "小强";
String s4 = "一起LOL";
startPointTime = System.currentTimeMillis();
strFormat2(s1, s2);
printTimeAndClear();
strFormat1();
printTimeAndClear();
strFormat3(s1, s2, s3, s4);
printTimeAndClear();
}

private static String strFormat3(String s1, String s2, String s3, String s4) {
StringBuilder sb = new StringBuilder();
sb.append(s1);
sb.append(s2);
sb.append(s3);
sb.append(s4);
return sb.toString();
}

private static String strFormat2(String s1, String s2) {
return s1 + "和" + s2 + "一起LOL";
}

private static String strFormat1() {
return String.format("%s和%s一起LOL", new String[]{ "小明", "小强"});
}

private static void printTimeAndClear() {
System.out.println(System.currentTimeMillis() - startPointTime);
startPointTime = System.currentTimeMillis();
}
}

输出结果:

三种字符串拼接方式比较_字符串

从上面的结果我们可以看出,在拼接字符串的时候使用format方式比较耗时,但是这样就得出结论还太早,下面我们来循环1000次看一下。

package com.codeing.snail.test;

public class StringFormat {
private static long startPointTime;
public static void main(String[] args) {
String s1 = "小明";
String s2 = "和";
String s3 = "小强";
String s4 = "一起LOL";
startPointTime = System.currentTimeMillis();
strFormat2(s1, s2);
printTimeAndClear();
//strFormat1();
//printTimeAndClear();
strFormat3(s1, s2, s3, s4);
printTimeAndClear();
}

private static String strFormat3(String s1, String s2, String s3, String s4) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 1000; i++){
sb.append(String.valueOf(i));
}
return sb.toString();
}

private static String strFormat2(String s1, String s2) {
String str = "begin";
for(int i = 0; i < 1000; i++){
str = str + String.valueOf(i);
}
return str;
}

private static String strFormat1() {
String str = "begin";
String[] strArr = new String[1000];
for(int i = 0; i < strArr.length; i++){
strArr[i] = String.valueOf(i);
}
//我勒个去,这不会让我写1000个他匹配符吧。。。显然这种方式不适合大量的拼接。
//return String.format("%s%s%s......", strArr);
return str;
}

private static void printTimeAndClear() {
System.out.println(System.currentTimeMillis() - startPointTime);
startPointTime = System.currentTimeMillis();
}
}

输出结果:

三种字符串拼接方式比较_字符串拼接_02

上面是用“+”号拼接字符串的结果,下面是使用StringBuilder的结果,很显然在拼接很频繁的情况下使用StringBuilder比使用“+”号拼接好,这种情况下使用format可以不予考虑。

综合比较:StringBuilder胜出,如果拼接的字符串比较多则可以优先考虑耗时。

二、从消耗内存角度看


这个角度我们就要从源代码寻找答案,先来看看StringBuilder的源码:



public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}

其中value是一个char[]类型,我们可以大概了解到实现字符串拼接的原理是字符数组的拷贝操作实现的。其实两个字符串相加的本质也是StringBuilder的方式相加的,但是会创建多余的字符串对象。

下面我们来看一下String.format的源码:



public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}

java.util.Formatter这个类可能大家比较熟悉,这个是格式化文本输出的类,比如日期、金额等,我们最常见的System.out.println()也是通过调用此类的format方法实现的。

Formatter的构造方法如下:



public Formatter() {
this(Locale.getDefault(Locale.Category.FORMAT), new StringBuilder());
}
/* Private constructors */
private Formatter(Locale l, Appendable a) {
this.a = a;
this.l = l;
this.zero = getZero(l);
}

可以看到创建了一个StringBuilder作为了Formatter的全局变量。


public Formatter format(Locale l, String format, Object ... args) {
ensureOpen();

// index of last argument referenced
int last = -1;
// last ordinary index
int lasto = -1;

FormatString[] fsa = parse(format);
for (int i = 0; i < fsa.length; i++) {
FormatString fs = fsa[i];
int index = fs.index();
try {
switch (index) {
case -2: // fixed string, "%n", or "%%"
fs.print(null, l);
break;
case -1: // relative index
if (last < 0 || (args != null && last > args.length - 1))
throw new MissingFormatArgumentException(fs.toString());
fs.print((args == null ? null : args[last]), l);
break;
case 0: // ordinary index
lasto++;
last = lasto;
if (args != null && lasto > args.length - 1)
throw new MissingFormatArgumentException(fs.toString());
fs.print((args == null ? null : args[lasto]), l);
break;
default: // explicit index
last = index - 1;
if (args != null && last > args.length - 1)
throw new MissingFormatArgumentException(fs.toString());
fs.print((args == null ? null : args[last]), l);
break;
}
} catch (IOException x) {
lastException = x;
}
}
return this;
}

在format方法中我们看到一个FormatString的print方法如下:


public void print(Object arg, Locale l)
throws IOException { a.append(s); }

a就是我们上面创建的StringBuilder,这下我们就明白了,原来String.format也是通过StringBuilder来时实现的。

通过上面分析我们可以得出这样的结论:StringBuilder是其他两种方式的基础实现,所以还是StringBuilder比较占优势。


三种字符串拼接方式比较_拼接字符串_03


综合比较:StringBuilder胜出,如果拼接的字符串比较多则可以优先考内存时。结合耗时我们可以得出这样的结论,在使用大量字符串拼接的时候优先使用StringBuilder.



三、从实际使用角度考虑


上面分析了两点,使用String.format和"+“号拼接字符串貌似毫无优势,那么为什么还要比较?其实对于有些场合(比如两个字符串拼接)我们为了程序的可读性、简洁等因素可以使用这两种方式。或者遇到要使用一些特殊格式的字符串(比如日期显示)或者进度刻度之间转换时我们就可以使用String.format来拼接。