从源码角度简单看StringBuilder和StringBuffer的异同
概述
StringBuilder和StringBuffer是两个容易混淆的概念,本文从源码入手,简单看二者的异同。
容易知道的是,这两者有一个是线程安全的,而且线程安全的那个效率低。
java doc里面的说明
java doc是写源码的人写的注释,先看java doc。
StringBuilder
A mutable sequence of characters. This class provides an API compatible with StringBuffer, but with no guarantee of synchronization. This class is designed for use as a drop-in replacement for StringBuffer in places where the string buffer was being used by a single thread (as is generally the case). Where possible, it is recommended that this class be used in preference to StringBuffer as it will be faster under most implementations.
The principal operations on a StringBuilder are the append and insert methods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string builder. The append method always adds these characters at the end of the builder; the insert method adds the characters at a specified point.
For example, if z refers to a string builder object whose current contents are "start", then the method call z.append("le") would cause the string builder to contain "startle", whereas z.insert(4, "le") would alter the string builder to contain "starlet".
In general, if sb refers to an instance of a StringBuilder, then sb.append(x) has the same effect as sb.insert(sb.length(), x).
Every string builder has a capacity. As long as the length of the character sequence contained in the string builder does not exceed the capacity, it is not necessary to allocate a new internal buffer. If the internal buffer overflows, it is automatically made larger.
Instances of StringBuilder are not safe for use by multiple threads. If such synchronization is required then it is recommended that java.lang.StringBuffer be used.
Unless otherwise noted, passing a null argument to a constructor or method in this class will cause a NullPointerException to be thrown.
Since:
1.5
Author:
Michael McCloskey
See Also:
java.lang.StringBuffer
java.lang.String
StringBuffer
A thread-safe, mutable sequence of characters. A string buffer is like a String, but can be modified. At any point in time it contains some particular sequence of characters, but the length and content of the sequence can be changed through certain method calls.
String buffers are safe for use by multiple threads. The methods are synchronized where necessary so that all the operations on any particular instance behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads involved.
The principal operations on a StringBuffer are the append and insert methods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string buffer. The append method always adds these characters at the end of the buffer; the insert method adds the characters at a specified point.
For example, if z refers to a string buffer object whose current contents are "start", then the method call z.append("le") would cause the string buffer to contain "startle", whereas z.insert(4, "le") would alter the string buffer to contain "starlet".
In general, if sb refers to an instance of a StringBuffer, then sb.append(x) has the same effect as sb.insert(sb.length(),
Whenever an operation occurs involving a source sequence (such as appending or inserting from a source sequence), this class synchronizes only on the string buffer performing the operation, not on the source. Note that while StringBuffer is designed to be safe to use concurrently from multiple threads, if the constructor or the append or insert operation is passed a source sequence that is shared across threads, the calling code must ensure that the operation has a consistent and unchanging view of the source sequence for the duration of the operation. This could be satisfied by the caller holding a lock during the operation's call, by using an immutable source sequence, or by not sharing the source sequence across threads.
Every string buffer has a capacity. As long as the length of the character sequence contained in the string buffer does not exceed the capacity, it is not necessary to allocate a new internal buffer array. If the internal buffer overflows, it is automatically made larger.
Unless otherwise noted, passing a null argument to a constructor or method in this class will cause a NullPointerException to be thrown.
As of release JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread, StringBuilder. The StringBuilder class should generally be used in preference to this one, as it supports all of the same operations but it is faster, as it performs no synchronization.
Since:
JDK1.0
Author:
Arthur van Hoff
See Also:
java.lang.StringBuilder
java.lang.String
javadoc小结
从上面可以看出:
- StringBuffer和StringBuilder都可以认为是可变的String。
- StringBuffer是线程安全的,先出现,在JDK1.0的时候就有了。
- StringBuilder是非线程安全的,后出现,在JDK1.5才有。
- 二者的接口完全一样,StringBuilder更快。
其实,正常的用,知道这几点就好了,不过还是想看看源码里面怎么实现的。
源码
如何实现线程安全
看源码可以知道,这二者都继承了一个抽象类AbstractStringBuilder
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
代码量也不是很大,StringBuilder440行代码,StringBuffer718行代码,最多的是AbstractStringBuilder,一共1440行代码。
看代码从几个角度看,一是一些关键的内部结构是怎么样的,二是我们常用的函数是怎么实现的。因为String是不可变的,是放在常量池里面的。猜测StringBuilder和StringBuffer应该是用char数组实现的。
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
可以看出,用value存数据,用一个count表示长度。
看几个常见方法的不同实现。
StringBuffer
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
/**
* @throws StringIndexOutOfBoundsException {@inheritDoc}
*/
@Override
public synchronized StringBuffer insert(int offset, String str) {
toStringCache = null;
super.insert(offset, str);
return this;
}
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
StringBuilder
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
/**
* @throws StringIndexOutOfBoundsException {@inheritDoc}
*/
@Override
public StringBuilder insert(int offset, String str) {
super.insert(offset, str);
return this;
}
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
由代码可知,大部分情况想,StrinbBuffer只是增加了一个synchronized关键字来保证线程安全。但toString方法不同,这个后面再说。
初始化大小和如何增长
既然实际上是一个数组,那么数组开始的大小和增长的方法就很重要,我们通过代码看一下。
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
可以看到,这两个默认构造函数都说明默认的数组大小是16。
为什么是16呢?我没想明白。
接下来关心如何增长的?我们看看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;
}
/**
* This method has the same contract as ensureCapacity, but is
* never synchronized.
*/
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
/**
* This implements the expansion semantics of ensureCapacity with no
* size check or synchronization.
*/
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);
}
上面三个方法说明了如何扩容。
- 把当前的容量*2+2
- 如果新增加的长度大于这个值,则设为新增加的值
- 如果溢出,则抛出OutOfMemoryError
StringBuffer中toString的实现
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
可以看到,定义了一个数组toStringCache,每次数据有变化,都把这个设置为null。在toString的时候,再重新从当前数据里面拿。
transient关键字是为了避免这个数组被序列化。
小结
其实java本身的源码写的还是比较简单的,学习知识如果能从源码入手,能够更深入的理解很多原理。本文只是简单的列举了一部分源码,简单说明StringBuffer和StringBuilder的异同。有兴趣的朋友可以自己看一下。
从源码角度简单看StringBuilder和StringBuffer的异同的更多相关文章
- 深入源码剖析String,StringBuilder,StringBuffer
[String,StringBuffer,StringBulider] 深入源码剖析String,StringBuilder,StringBuffer [作者:高瑞林] [博客地址]http://ww ...
- [源码分析] 从源码入手看 Flink Watermark 之传播过程
[源码分析] 从源码入手看 Flink Watermark 之传播过程 0x00 摘要 本文将通过源码分析,带领大家熟悉Flink Watermark 之传播过程,顺便也可以对Flink整体逻辑有一个 ...
- 从JDK源码角度看java并发的公平性
JAVA为简化开发者开发提供了很多并发的工具,包括各种同步器,有了JDK我们只要学会简单使用类API即可.但这并不意味着不需要探索其具体的实现机制,本文从JDK源码角度简单讲讲并发时线程竞争的公平性. ...
- 从JDK源码角度看Short
概况 Java的Short类主要的作用就是对基本类型short进行封装,提供了一些处理short类型的方法,比如short到String类型的转换方法或String类型到short类型的转换方法,当然 ...
- 从JDK源码角度看Byte
Java的Byte类主要的作用就是对基本类型byte进行封装,提供了一些处理byte类型的方法,比如byte到String类型的转换方法或String类型到byte类型的转换方法,当然也包含与其他类型 ...
- 从JDK源码角度看Object
Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类.它包含了对象常用的一些方法,比如getClass.hashCode.equals.clone. ...
- 从JDK源码角度看Boolean
Java的Boolean类主要作用就是对基本类型boolean进行封装,提供了一些处理boolean类型的方法,比如String类型和boolean类型的转换. 主要实现源码如下: public fi ...
- JDK源码分析(8) StringBuffer & StringBuilder
简介 StringBuffer与StringBuilder是两个常用的操作字符串的类.大家都知道,StringBuilder是线程不安全的,而StringBuffer是线程安全的.前者是JDK1.5加 ...
- 源码上看 .NET 中 StringBuilder 拼接字符串的实现
前几天写了一篇StringBuilder与TextWriter二者之间区别的文章(链接).当时提了一句没有找到相关源码,于是随后有很多热心人士给出了相关的源码链接(链接),感谢大家.这几天抽了点时间查 ...
随机推荐
- java历史概述
java简介Java是由Sun Microsystems公司于 1995年5月推出的Java面向对象程序设计语言(以下简称Java语言)和Java平台的总称.由James Gosling和同事们共同研 ...
- 无法将类型为“System.DBNull”的对象强制转换为类型“System.String”
在ERP中做业务类单据,有时候会遇到这样的报错. 无法将类型为"System.DBNull"的对象强制转换为类型"System.String" 去数据库中检 ...
- 使用numpy实现批量梯度下降的感知机模型
生成多维高斯分布随机样本 生成多维高斯分布所需要的均值向量和方差矩阵 这里使用numpy中的多变量正太分布随机样本生成函数,按照要求设置均值向量和协方差矩阵.以下设置两个辅助函数,用于指定随机变量维度 ...
- Android开发之漫漫长途 Ⅵ——图解Android事件分发机制(深入底层源码)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- (ajax)——jquery用法
例子:/* ajax获得状态 */ 点击事件 $("#findBycname").click(function(){ var company = ...
- 用echartsjs 实现散点图与table表格双向交互,以及实现echarts取自于table数据,和自定义echarts提示内容
本人研究echarts已经有一段时间了,今天就分享几个关于echarts的小技巧.虽然看起来简单,但做起来却很繁琐,不过实用性倒是很好. 在一个大的页面中,左边为table表格,右边为echarts的 ...
- 简单模板view调用
$this->display();//模板名称与当前控制器.当前操作方法的名称不一致 $this->display();//调用当前控制器对应目录指定名称的模板 $this->dis ...
- POJ 1151 Wormholes spfa+反向建边+负环判断+链式前向星
Wormholes Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 49962 Accepted: 18421 Descr ...
- PHP魔术方法和魔术变量总结
从php5以后的版本,类就可以使用魔术方法了.PHP规定以两个下划线开头的方法都保留为魔术方法,所以建议大家函数名最好不用__开 头,除非是为了重载已有的魔术方法. 目前php已有的魔术方法有 __c ...
- javascript设计模式——代理模式
前面的话 代理模式是为一个对象提供一个占位符,以便控制对它的访问. 代理模式是一种非常有意义的模式,在生活中可以找到很多代理模式的场景.比如,明星都有经纪人作为代理.如果想请明星来办一场商业演出,只能 ...