Java高效字符串操作之StringBuilder源码级解析

字符串不是有String吗?还要StringBuilder干嘛啊?

其实StringBuilder准确的来说是字符串缓冲区。String定义的字符串是常量,创建后不可变因为String的底层源码是:

@Stable
private final byte[] value;

final修饰还变个🔨啊,但是字符串缓冲区却支持可变字符串。因为其底层源码为。

private byte[] value;

没有被final修饰。


StringBuilder在做字符串拼接时其效率很高,较String无脑式加法操作来说,举个例子:

String str = "Author"+" is"+" handsome!";

> Author is handsome!

输出以上内容的步骤可以分解为:

String str = "Author"+" is"+" handsome!";
/*
*Step1:  "Author"+" is"+" handsome!" 三个字符串 
*
*Step2:  "Author is" + "handsome!"  此时一个新的字符串产生Author is
*
*Step:   "Author is handsome!"   此时拼接完成,又产生最后一个结果字符串。
*/

在这个过程中,中间的两两相加,会占用内存,如果需要拼接的元素很多,就会明显变慢。而StringBuilder中的append方法会使这个过程提速很多。(后面会具体解释为什么快)

StringBuilder类

StringBuilder类又叫字符串缓冲区,可以看作长度可变的字符串。长度可变是因为其底层源码未使用final修饰。

具体使用方法分为两种(有参/无参):

  • public StringBuilder():构造一个空StringBuilder容器(长度为16)。
  • public StringBuilder(String str):构造一个StringBuilder容器并同时添加字符串。
//StringBuilder源码
public StringBuilder() {
	super(16);
}

public StringBuilder(int capacity) {
    super(capacity);
}

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
}

public StringBuilder(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}

//AbstractStringBuilder源码   (StringBuilder的父类)
//成员变量
byte[] value;
byte coder;
int count;
//无参构造
AbstractStringBuilder() {
}

//有参构造
AbstractStringBuilder(int capacity) {
    if (COMPACT_STRINGS) {
        value = new byte[capacity];
        coder = LATIN1;
    } else {
        value = StringUTF16.newBytesFor(capacity);
        coder = UTF16;
    }
}

阅读上述代码可知,使用无参构造StringBuilder出的字符串,里面“什么都没有”,其默认长度为16

StringBuilder类的父类是一个抽象类,同时也作为AppendableCharSequence接口的实现:

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    .... ....
}
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence{
    .... ....
}

StringBuilder类不但继承了父类中部分方法,也重写了其中的一部分。

@Override
public StringBuilder append(Object obj) {
    return append(String.valueOf(obj));
}

@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(String str) {
   super.append(str);
   return this;
}
// .... ....

public StringBuilder append(StringBuffer sb) {
    super.append(sb);
    return this;
}
// .... ....

由此可知,StringBuilder中的append()方法支持多种数据类型参数(重载)。


append()方法

上文提到的对于字符串拼接效率高于传统的String加法:

//下面为两种String类型的字符串拼接过程
String str1 = "Surprise ";
String str2 = "Mother "; 
String str3="Fucker!";
String str4 = str1 + str2 + str3;
System.out.println(str4);
//>Surprise Mother Fucker!
//或:
String str = "Hello "+"Java";
System.out.println(str);
//>Hello Java

使用StringBuilder内置append()方法来执行上面的操作效率会高很多。具体原因在于,StringBuilder中的append()方法,每次返回值不是一个新的对象,而是其对象执行操作后的本身(this),没有中间量的生成就不会占用额外的内存去存储中间过程中产生的值。具体体现源码为:

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

public StringBuilder append(StringBuffer sb) {
    super.append(sb);
    return this;
}

上面是该类的源码,可见每次返回值均为this,每次操作也均无中间值产生。

我们可以进行一个简短的验证:

public static void main(String[] args){
    //代参数初始化创建StringBuilder对象为字符串"biu~"
    StringBuilder biu = new StringBuilder("biu~");
    System.out.println(biu);
    //向该类型字符串后添加"biu~"
    StringBuilder biubiu = biu.append("biu~");
    System.out.println(biubiu);
    //再添加"biu~"
    StringBuilder biubiubiu = biu.append("biu~");
    System.out.println(biubiubiu);
}

运行的结果为:

在这里插入图片描述

为了确定其每次操作都没有产生新的值,而是只对该对象自身操作,我们可以比较他们的地址值,来确定是否发生改变。

public static void main(String[] args){
    //代参数初始化创建StringBuilder对象为字符串"biu~"
    StringBuilder biu = new StringBuilder("biu~");
    System.out.println(biu);
    //向该类型字符串后添加"biu~"
    StringBuilder biubiu = biu.append("biu~");
    System.out.println(biubiu);
    //再添加"biu~"
    StringBuilder biubiubiu = biu.append("biu~");
    System.out.println(biubiubiu);
    //----------------Cross Line------------------
    boolean flag1 = biu == biubiu;
    boolean flag2 = biubiu == biubiubiu;
    System.out.println("biu_ADDR == biubiu_ADDR ? " + flag1);
    System.out.println("biubiu_ADDR == biubiubiu_ADDR ? " + flag2);  
}

结果如下:

在这里插入图片描述

其地址值相等,可见期间该地址值为发生改变,没有所谓的中间步骤。是对对象本身进行操作,最后又返回该对象本身(return this;

由此点特性我们还能知道,使用该方法来操作拼接字符串,不需要接收返回值,因为append()方法本质就是修改StringBuidler对象,最后返回该对象。

由于每次append()都会return this的特点,我们可以对StringBuilder对象进行链式编程:

public static void main(String[] args){
    //初始化为空,也可带参数初始化。
    StringBuilder sb = new StringBuilder();
    //可以写在一行,这样写是为了结构清晰
    sb.append("1")
      .append("2")
      .append("3")
      .append("4")
      .append("5");
    System.out.println(sb);
} 

运行结果:

在这里插入图片描述


String 与 StringBuilder

String和StringBuilder之间可以相互转换

String to StringBuilder

使用StringBuilder的构造方法:public StringBuilder(String str)构造一个StringBuilder容器并同时添加字符串。

StringBuilder to String

使用StringBuilder的toString方法,在源码中StringBuilder重写(Override)了toSting方法。

public static void mian(String[] args){
    
//String --> StringBuilder    
	String str1 = "ooaa";
    StringBuilder sb = new StringBuilder(str1);

    
//StringBuilder --> String 
    //匿名对象的StringBuilder
    String str2 = new StringBuilder("ooaa").toString;
    
	//命名对象的StringBuilder
    StringBuilder oa = new StringBuilder("ooaa");
    String str3 = oa.toString();
    
}

由于这个方法比较简单,这里不作过多赘述。关于StringBuilder的介绍就到这里,本文只详细介绍了StringBuilder类中常用的两种方法,StringBuilder类中还有很多骚操作:

@Override
public StringBuilder delete(int start, int end)

@Override
public StringBuilder deleteCharAt(int index)

@Override
public StringBuilder replace(int start, int end, String str)

@Override
public int indexOf(String str)
.... ....

如果你已经理解上述方法,可以再看一看上述方法在StringBuilder类的源码中的定义。(PS:以上方法都被重写过,返回值均为StringBuidler对象本身,这就是为什么说使用StringBuilder类中方法对字符串进行操作效率会高很多!)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值