String相关的四个字符串类

本篇文章介绍String,StringBuilder,StringBuffer和StringJoner的区别和使用,以及如何进行字符串拼接的常用方法

String

String类被final修饰,不可被继承,内部由一个被final字符数组实现,因此String是一个不可变类,这意味着对String的每次修改都会创建新的存储空间,而StringBuilder,StringBuffer都是字符串变量,是可以修改的。

如果执行String a=”abc”+”def”;,实际上经历了以下过程:

  1. 创建“abc”
  2. 创建“def”
  3. 合成“abcdef”
  4. 赋值给a
    String拼接过程

至于”+”号是如何实现拼接的,后面会讲到。

StringBuilder和StringBuffer

之所以把这两个合在一起说是因为它们基本用法都类似,它们都是字符串变量,即它们的值一般可以动态改变而不会多占据存储空间。

JDK1.8中对StringBuilder和StringBuffer定义为AbstractStringBuilder的子类,这也说明了两者有巨大的相似性,AbstractStringBuilder定义为如下结构(StringBuilder始于JDK1.5):
StringBuilder内部结构

字符串数组value不被final修饰,证明value可变;由于存在count表示实际字符个数,这意味着value中的数据不是充满了整个value空间。

StringBuilder和StringBuffer的区别在于


1. StringBuilder是非线程安全的,StringBuffer是线程安全的
2. StringBuilder速度快,StringBuffer速度慢

StringBuilder和StringBuffer常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

package test.string;
public class StringTest {
public static void main(String[] args) {
StringBuilder builder=new StringBuilder();
/*append()方法*/
builder.append("hi ").append("my name is ").append("smartpig");
System.out.println(builder.toString());
/*insert()方法*/
builder.insert(2, ",");
System.out.println(builder.toString());
/*replace()方法*/
builder.replace(0, 2, "hello");
System.out.println(builder.toString());
/*reverse()方法*/
builder.reverse();
System.out.println(builder.toString());
/*其他方法都和String类似*/
}
}

结果如下:
result

append()方法功能和“+“一样,用来拼接字符串,具体实现下面会讲到。

StringBuffer和StringBuilder方法一样,唯一区别在于StringBuffer是线程安全的。

字符串拼接

方法1 “+“语法糖

使用最多的“+“其实是一种Java的语法糖,语法糖被定义为:对语言的功能没有影响但能方便程序员使用的一种语法。

“+“的内部实现其实最终依赖于StringBuilder,还是上面的例子String a=”abc”+”def”;
底层真正的实现是:String a=(new StringBuilder).append(“abc”).append(“def”).toString();

内部使用了StringBuilder,而且每次都会新建一个StringBuilder对象,大量操作必然导致资源浪费,所以在阿里巴巴开发手册中就明确指出:

循环体内,字符串的连接方式,使用StringBuilder的append()方法进行扩展

方法2 concat()方法

String.concat()也是字符串拼接的一种方法,但没有“+“使用广泛

concat()内部又是如何实现?查阅JDK源码:

1
2
3
4
5
6
7
8
9
10
11

public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}

主要思想是创建一个新的字符数组,长度为当前字符串和待拼接字符串之后,然后将两个字符串复制到新的字符数组中,并用新的字符数组new一个新的String,这也印证了String是不可变的字符串。

方法3 append()方法

StringBuilder和StringBuffer的append()方法基本类似,以StringBuilder为例:

1
2
3
4
5
6

public StringBuilder append(String str) {
super.append(str);
return this;
}


内部调用父类的append()方法,AbstractStringBuilder的append()方法定义如下:
1
2
3
4
5
6
7
8
9
10
11

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;
}

append()方法直接将待拼接字符串拷贝到内部的value数组中,然后修改count的值;当然在此之前,首先要保证value的容量足够,如果不够,会先进行扩容,再拷贝字符串。

方法4 使用StringJoiner

StringJoiner是一个由于构造由分隔符和前后缀组成的字符串。
与其他字符串不同的是,StringJoiner的构造方法只有两个,不存在空参构造函数:

  1. 第一个构造函数有一个参数,表示指定的分隔符
  2. 第二个构造函数有三个参数,表示指定的分隔符和指定的前缀和后缀

具体使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

package test.string;
import java.util.StringJoiner;
public class StringJoinerTest {
public static void main(String[] args) {
//第一种构造函数
StringJoiner joiner1=new StringJoiner("/");
joiner1.add("2019").add("04").add("11");
System.out.println(joiner1.toString());
//第二种构造函数
StringJoiner joiner2=new StringJoiner(",","[","]");
joiner2.add("Java").add("Python").add("Kotlin");
System.out.println(joiner2.toString());
}
/*结果如下:
* 2019/04/11
* [Java,Python,Kotlin]
*/
}

上面的例子非常清楚的展示了如何使用StringJonier的构造函数,以及如何使用add()方法进行字符串拼接。StringJoiner的拼接字符串非常灵活也很有用处。

StringJoiner的add()方法内部的实现也是基于StringBuilder.append()方法

1
2
3
4
5
6

public StringJoiner add(CharSequence newElement) {
prepareBuilder().append(newElement);
return this;
}

其中的prepareBuilder()返回的是一个StringBuilder对象,再调用prepareBuilder()后,完全是基于StringBuilder来实现的

1
2
3
4
5
6
7
8
9
10

private StringBuilder prepareBuilder() {
if (value != null) {
value.append(delimiter);
} else {
value = new StringBuilder().append(prefix);
}
return value;
}

但是并没有看到对后缀的处理,其实后缀的处理放在了toString()方法中。

StringJoiner使用StringBuilder,效率和StringBuilder不相上下,是非线程安全的。

总结

  1. 简单字符串拼接使用“+“
  2. 循环体内字符串拼接使用StringBuilder/StringBuffer
  3. 特殊格式字符串拼接使用StringJoiner
  4. 多线程下使用StringBuffer
smartpig wechat
扫一扫关注微信公众号
0%