Java StringBuilder和StringBuffer源码分析

作者:然则 时间:2023-04-03 00:10:58 

StringBuilder与StringBuffer是两个常用的操作字符串的类。大家都知道,StringBuilder是线程不安全的,而StringBuffer是线程安全的。前者是JDK1.5加入的,后者在JDK1.0就有了。下面分析一下它们的内部实现。

一、继承关系


public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence

public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence

可以看到,两个类的继承关系是一模一样的。Serializable是可以序列化的标志。CharSequence接口包含了charAt()、length() 、subSequence()、toString()这几个方法,String类也实现了这个接口。这里的重点是抽象类AbstractStringBuilder,这个类封装了StringBuilder和StringBuffer大部分操作的实现。

二、AbstractStringBuilder

1、变量及构造方法


char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
 value = new char[capacity];
}

AbstractStringBuilder内部用一个char[]数组保存字符串,可以在构造的时候指定初始容量方法。

2、扩容


public void ensureCapacity(int minimumCapacity) {
 if (minimumCapacity > 0)
   ensureCapacityInternal(minimumCapacity);
}
private void ensureCapacityInternal(int minimumCapacity) {
 // overflow-conscious code
 if (minimumCapacity - value.length > 0)
   expandCapacity(minimumCapacity);
}
void expandCapacity(int minimumCapacity) {
 int newCapacity = value.length * 2 + 2;
 if (newCapacity - minimumCapacity < 0)
   newCapacity = minimumCapacity;
 if (newCapacity < 0) {
   if (minimumCapacity < 0) // overflow
     throw new OutOfMemoryError();
   newCapacity = Integer.MAX_VALUE;
 }
 value = Arrays.copyOf(value, newCapacity);
}

扩容的方法最终是由expandCapacity()实现的,在这个方法中首先把容量扩大为原来的容量加2,如果此时仍小于指定的容量,那么就把新的容量设为minimumCapacity。然后判断是否溢出,如果溢出了,把容量设为Integer.MAX_VALUE。最后把value值进行拷贝,这显然是耗时操作。

3、append()方法


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()是最常用的方法,它有很多形式的重载。上面是其中一种,用于追加字符串。如果str是null,则会调用appendNull()方法。这个方法其实是追加了'n'、'u'、'l'、'l'这几个字符。如果不是null,则首先扩容,然后调用String的getChars()方法将str追加到value末尾。最后返回对象本身,所以append()可以连续调用。

三、StringBuilder

AbstractStringBuilder已经实现了大部分需要的方法,StringBuilder和StringBuffer只需要调用即可。下面来看看StringBuilder的实现。

1、构造器


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

可以看出,StringBuilder默认的容量大小为16。当然也可以指定初始容量,或者以一个已有的字符序列给StringBuilder对象赋初始值。

2、append()方法


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

append()的重载方法很多,这里随便列举了两个。显然,这里是直接调用的父类AbstractStringBuilder中的方法。

3、toString()


public String toString() {
 // Create a copy, don't share the array
 return new String(value, 0, count);
}

toString()方法返回了一个新的String对象,与原来的对象不共享内存。其实AbstractStringBuilder中的subString()方法也是如此。

四、SringBuffer

StiringBuffer跟StringBuilder类似,只不过为了实现同步,很多方法使用lSynchronized修饰,如下面的方法:


public synchronized int length() {
   return count;
}
public synchronized StringBuffer append(String str) {
 toStringCache = null;
 super.append(str);
 return this;
}
public synchronized void setLength(int newLength) {
 toStringCache = null;
 super.setLength(newLength);
}

可以看到,方法前面确实加了Synchronized。
另外,在上面的append()以及setLength()方法里面还有个变量toStringCache。这个变量是用于最近一次toString()方法的缓存,任何时候只要StringBuffer被修改了这个变量会被赋值为null。StringBuffer的toString如下:


public synchronized String toString() {
 if (toStringCache == null) {
   toStringCache = Arrays.copyOfRange(value, 0, count);
 }
 return new String(toStringCache, true);
}

在这个方法中,如果toStringCache为null则先缓存。最终返回的String对象有点不同,这个构造方法还有个参数true。找到String的源码看一下:


String(char[] value, boolean share) {
 // assert share : "unshared not supported";
 this.value = value;
}

原来这个构造方法构造出来的String对象并没有实际复制字符串,只是把value指向了构造参数,这是为了节省复制元素的时间。不过这个构造器是具有包访问权限,一般情况下是不能调用的。

总结

  • StringBuilder和StringBuffer都是可变字符串,前者线程不安全,后者线程安全。

  • StringBuilder和StringBuffer的大部分方法均调用父类AbstractStringBuilder的实现。其扩容机制首先是把容量变为原来容量的2倍加2。最大容量是Integer.MAX_VALUE,也就是0x7fffffff。

  • StringBuilder和StringBuffer的默认容量都是16,最好预先估计好字符串的大小避免扩容带来的时间消耗。

标签:Java,StringBuilder,StringBuffer
0
投稿

猜你喜欢

  • Java中Excel高效解析工具EasyExcel的实践

    2023-05-28 13:36:38
  • Android调用系统摄像头拍照并显示在ImageView上

    2022-02-19 17:42:00
  • mybatis plus中如何编写sql语句

    2021-09-21 15:27:55
  • Repeater中添加按钮实现点击按钮获取某一行数据的方法

    2022-05-17 08:19:31
  • Android XListView下拉刷新和上拉加载更多

    2022-11-01 19:07:45
  • Springboot自带定时任务实现动态配置Cron参数方式

    2023-11-10 10:21:31
  • 基于maven的三种packaging方式

    2021-09-30 22:15:05
  • Android仿京东分类模块左侧分类条目效果

    2022-09-09 04:35:34
  • Android编程基础之获取手机屏幕大小(DisplayMetrics应用)示例

    2023-09-26 17:57:43
  • 听说用了YYYY-MM-dd的程序员,前些天都在加班改Bug

    2023-07-05 17:48:00
  • c#基于WinForm的Socket实现简单的聊天室 IM

    2021-11-27 04:47:57
  • C#事件标准命名规则及说明(包括用作事件类型的委托命名)

    2022-02-27 06:57:43
  • Android开发实现Launcher3应用列表修改透明背景的方法

    2023-09-28 08:45:27
  • 浅谈一下Java的双亲委派模式

    2021-11-12 02:48:17
  • C#设置MDI子窗体只能弹出一个的方法

    2022-03-19 23:40:11
  • Android实现价格走势自定义曲线图

    2023-04-03 23:15:06
  • SpringBoot整合mybatis-generator-maven-plugin的方法

    2022-08-14 18:34:22
  • java中Cookie被禁用后Session追踪问题

    2023-10-17 13:44:32
  • SpringMVC使用hibernate-validator进行参数校验最佳实践记录

    2022-11-12 23:44:04
  • c# 曲线图生成代码

    2023-03-27 07:52:55
  • asp之家 软件编程 m.aspxhome.com