深入学习java源码之StringBuilder.append()与StringBuilder.appendCodePoint()
CharSequence接口
charSequence是一个接口,表示char值的一个可读序列。此接口对许多不同种类的char序列提供统一的自读访问。此接口不修改该equals和hashCode方法的常规协定,因此,通常未定义比较实现 CharSequence 的两个对象的结果。他有几个实现类:CharBuffer、String、StringBuffer、StringBuilder。
CharSequence与String都能用于定义字符串,但CharSequence的值是可读可写序列,而String的值是只读序列。
对于一个抽象类或者是接口类,不能使用new来进行赋值,但是可以通过以下的方式来进行实例的创建:
CharSequence b = "s";
CharSequence cs=”hello”;
通常我们都把它等价于String类型,将参数使用toString()等方法转换为String再传入。实际上,CharSequence是一个接口,String、StringBuilder和StringBuffer都实现了CharSequence接口,所以以后再调用含有CharSequence类型参数的方法时,可以直接将StringBuilder或StringBuffer直接传入进去,减少一次转换。另外,有些工具类中跟字符串相关的类型,也会实现这个接口,便于减少方法调用直接传参。
Appendable接口
Appendable接口的实现类的对象能够被添加 char 序列和值。如果某个类的实例打算接收取自 java.util.Formatter 的格式化输出,那么该类必须实现 Appendable 接口。格式化主要用在文本输出方面,比如,数字、日期、金额等
要添加的字符应该是有效的 Unicode 字符。
Appendable 对于多线程访问而言没必要是安全的。线程安全由扩展和实现此接口的类负责。
所有已知实现类:
BufferedWriter, CharArrayWriter, CharBuffer, FileWriter, FilterWriter, LogStream, OutputStreamWriter, PipedWriter, PrintStream, PrintWriter, StringBuffer, StringBuilder, StringWriter, Writer
java的super与this关键字
super关键字
在JAVA类中使用super来引用父类的成分,用this来引用当前对象,如果一个类从另外一个类继承,我们new这个子类的实例对象的时候,这个子类对象里面会有一个父类对象。怎么去引用里面的父类对象呢?使用super来引用,this指的是当前对象的引用,super是当前对象里面的父对象的引用。
super关键字有两种用法:
1 在子类的构造方法中使用super关键字调用直接父类的构造方法,super语句必须是该子类构造方法中的第一句 。如果在子类中没有使用super关键字,系统会默认调用父类没有参数的构造方法,即隐含的使用super();语句,注意这时父类一定应该有一个无参数的构造方法。
2 使用super关键字调用被子类隐藏的成员变量和方法。如果子类定义了和父类具有相同名字的成员变量,子类就隐藏了从父类继承的成员变量。如果子类定义的某个方法与父类定义的某个方法完全相同,从父类继承的该方法就被完全隐藏(即override,覆盖)。
this关键字
this关键字可以出现在构造方法和实例方法中,不能出现在静态方法中,这是因为静态方法可以用类名来调用,这时可能还没有任何对象诞生。
this主要有两种用法:
1 用在构造方法中,调用本类的其他构造方法。 格式为 this([实参列表]); 通过this调用本类的其他构造方法时,该语句必须出现在构造方法中,并且必须作为第一条语句出现。
2 代指当前对象
StringBuilder的append方法
StringBuilder sb = new StirngBuilder();
sb.append("a").append("b").append("c").append("d");
String str = "";
str += "a";
str += "b";
str += "c";
str += "d";
上面的例子中,“a”、“b”、“c”、“d”都应该会首先在常量池中分配空间,是第一段代码效率更高
而str + "a" 会创建一个新的String对象,就慢了。你要知道String对象一旦创建就是不能被改变的,要达到字符串拼接的效果,就得不停创建新对象,而创建新的对象时,有可能会触发GC。
StringBuilder直到最后sb.toString()才会创建String对象,之前都没有创建新对象,但是如果你append的总长度超过一定范围——默认是16——就会创建一个新的数组,来装下更多的String。
也就是说 快得原因就是因为StringBuilder预先开辟了空间, append的时候只是向内存地址赋值; 而String总要不断的现开辟空间. 也因此String占的空间也会相对大。
StringBuilder和StringBuffer,字符串是存放在char[]中的,char[]是存放在堆中的。
相比String每次+都重新创建一个String对象,重新开辟一段内存不同,StringBuilder和StringBuffer的append都是直接把String对象中的char[]的字符直接拷贝到StringBuilder和StringBuffer的char[]上,效率比String的+高得多。当然,当StringBuilder和StringBuffer的char[]长度不够时,也会重新开辟一段内存。
另外,StringBuffer是线程安全的,StringBuilder不是线程安全的。
StringBuilder()的高性能
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
static final long serialVersionUID = 4383685877147921099L;
public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
}
父类AbstractStringBuilder 中的AbstractStringBuilder(int capacity)构造方法
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
}
StringBuilder的内部有一个char[], 在调用StringBuilder的无参构造方法时其内部char[]的默认长度是16。当我们调用StringBuilder的append方法时,其实就是不断的往char[]里填东西的过程。
public StringBuilder append(String str) {
super.append(str);
return this;
}
其中,super.append是调用AbstractStringBuilder 的append(String str)方法,如下:
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
StringBuilder的扩容和ArrayList有些类似
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);
}
StringBuilder默认长度是16,然后,如果要append第17个字符,怎么办?
答案是采用 Arrays.copyOf()成倍复制扩容!
扩容的性能代价是很严重的:一来有数组拷贝的成本,二来原来的char[]也白白浪费了要被GC掉。可以想见,一个129字符长度的字符串,经过了16,32,64, 128四次的复制和丢弃,合共申请了496字符的数组,在高性能场景下,这几乎不能忍。
由此可见,合理设置一个初始值多重要。使用之前先仔细评估一下要保存的字符串最大长度。
复用StringBuilder
StringBuilder.setLength()方法只重置它的count指针,而char[]则会继续重用
public void setLength(int newLength) {
if (newLength < 0)
throw new StringIndexOutOfBoundsException(newLength);
ensureCapacityInternal(newLength);
if (count < newLength) {
for (; count < newLength; count++)
value[count] = '\0';
} else {
count = newLength;
}
}
toString()方法:
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
而toString()时会把当前的count指针也作为参数传给String的构造函数,所以不用担心把超过新内容大小的旧内容也传进去了。可见,StringBuilder是完全可以被重用的。
String[] history_steps = ruleDetailInfo.getHistory_steps().split(",");
for (String step : history_steps){
sb.setLength(0);
sb.append(ruleDetailInfo.getBusiness_id()).append("\t").append(step);
results.add(sb.toString());
}
+ 与 StringBuilder的区别
String s = "hello" + user.getName();
这一行代码经过javac编译后的效果,的确等价于使用StringBuilder,但没有设定长度。
String s = new StringBuilder().append(“hello”).append(user.getName());
但是,如果像下面这样:
String s = “hello ”;
// 中间插入了其他一些代码
s = s + user.getName();
每一条语句,都会生成一个新的StringBuilder,这里就有了两个StringBuilder,性能就完全不一样了。
如果是在循环体里s+=i; 就更加多得没谱,例如:
String str = "";
for(int i=0; i<10000;i++){
str += i;
}
StringBuffer与StringBuilder都是继承于AbstractStringBuilder,唯一的区别就是StringBuffer的函数上都有synchronized关键字。
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {
/** use serialVersionUID from JDK 1.0.2 for interoperability */
static final long serialVersionUID = 3388685877147921107L;
public StringBuffer() {
super(16);
}
public StringBuffer(int capacity) {
super(capacity);
}
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
public StringBuffer(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
public synchronized int length() {
return count;
}
public synchronized int capacity() {
return value.length;
}
public synchronized void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > value.length) {
expandCapacity(minimumCapacity);
}
}
public synchronized void setLength(int newLength) {
super.setLength(newLength);
}
public synchronized StringBuffer append(Object obj) {
super.append(String.valueOf(obj));
return this;
}
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
......
}
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {
/** use serialVersionUID for interoperability */
static final long serialVersionUID = 4383685877147921099L;
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);
}
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
public StringBuilder append(String str) {
super.append(str);
return this;
}
// Appends the specified string builder to this sequence.
private StringBuilder append(StringBuilder sb) {
if (sb == null)
return append("null");
int len = sb.length();
int newcount = count + len;
if (newcount > value.length)
expandCapacity(newcount);
sb.getChars(0, len, value, count);
count = newcount;
return this;
}
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}
......
}
StringBuilder是非线程安全的,所以不能在多线程环境下共享使用。StringBuilder在使用的时候一定要指定其初始大小,另外,对性能要求比较高的场景下,可以考虑用一个ThreadLocal 缓存可重用的StringBuilder。
下划线转驼峰
public static String underlineToCamel(String param){
if (param==null||"".equals(param.trim())){
return "";
}
int len=param.length();
StringBuilder sb=new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c = Character.toLowerCase(param.charAt(i));
if (c == UNDERLINE){
if (++i
Modifier and Type | Method and Description |
---|---|
StringBuilder |
append(boolean b) 将 |
StringBuilder |
append(char c) 将 |
StringBuilder |
append(char[] str) 将 |
StringBuilder |
append(char[] str, int offset, int len) 将 |
StringBuilder |
append(CharSequence s) 将指定的字符序列追加到此 Appendable 。 |
StringBuilder |
append(CharSequence s, int start, int end) 追加指定的序列 |
StringBuilder |
append(double d) 将 |
StringBuilder |
append(float f) 将 |
StringBuilder |
append(int i) 将 |
StringBuilder |
append(long lng) 将 |
StringBuilder |
append(Object obj) 追加 |
StringBuilder |
append(String str) 将指定的字符串附加到此字符序列。 |
StringBuilder |
append(StringBuffer sb) 将指定 |
StringBuilder |
appendCodePoint(int codePoint) 将 |
int |
capacity() 返回当前容量。 |
char |
charAt(int index) 返回 |
void |
ensureCapacity(int minimumCapacity) 确保容量至少等于规定的最小值。 |
void |
getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 字符从该序列复制到目标字符数组 |
java源码
package java.lang;
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
static final long serialVersionUID = 4383685877147921099L;
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);
}
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}
@Override
public StringBuilder append(CharSequence s) {
super.append(s);
return this;
}
@Override
public StringBuilder append(CharSequence s, int start, int end) {
super.append(s, start, end);
return this;
}
@Override
public StringBuilder append(char[] str) {
super.append(str);
return this;
}
@Override
public StringBuilder append(char[] str, int offset, int len) {
super.append(str, offset, len);
return this;
}
@Override
public StringBuilder append(boolean b) {
super.append(b);
return this;
}
@Override
public StringBuilder append(char c) {
super.append(c);
return this;
}
@Override
public StringBuilder append(int i) {
super.append(i);
return this;
}
@Override
public StringBuilder append(long lng) {
super.append(lng);
return this;
}
@Override
public StringBuilder append(float f) {
super.append(f);
return this;
}
@Override
public StringBuilder append(double d) {
super.append(d);
return this;
}
@Override
public StringBuilder appendCodePoint(int codePoint) {
super.appendCodePoint(codePoint);
return this;
}
}
package java.lang;
import sun.misc.FloatingDecimal;
import java.util.Arrays;
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
@Override
public int length() {
return count;
}
public int capacity() {
return value.length;
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
{
if (srcBegin < 0)
throw new StringIndexOutOfBoundsException(srcBegin);
if ((srcEnd < 0) || (srcEnd > count))
throw new StringIndexOutOfBoundsException(srcEnd);
if (srcBegin > srcEnd)
throw new StringIndexOutOfBoundsException("srcBegin > srcEnd");
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
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;
}
public AbstractStringBuilder append(StringBuffer sb) {
if (sb == null)
return appendNull();
int len = sb.length();
ensureCapacityInternal(count + len);
sb.getChars(0, len, value, count);
count += len;
return this;
}
AbstractStringBuilder append(AbstractStringBuilder asb) {
if (asb == null)
return appendNull();
int len = asb.length();
ensureCapacityInternal(count + len);
asb.getChars(0, len, value, count);
count += len;
return this;
}
public AbstractStringBuilder append(CharSequence s) {
if (s == null)
return appendNull();
if (s instanceof String)
return this.append((String)s);
if (s instanceof AbstractStringBuilder)
return this.append((AbstractStringBuilder)s);
return this.append(s, 0, s.length());
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
@Override
public AbstractStringBuilder append(CharSequence s, int start, int end) {
if (s == null)
s = "null";
if ((start < 0) || (start > end) || (end > s.length()))
throw new IndexOutOfBoundsException(
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start;
ensureCapacityInternal(count + len);
for (int i = start, j = count; i < end; i++, j++)
value[j] = s.charAt(i);
count += len;
return this;
}
public AbstractStringBuilder append(char[] str) {
int len = str.length;
ensureCapacityInternal(count + len);
System.arraycopy(str, 0, value, count, len);
count += len;
return this;
}
public AbstractStringBuilder append(char str[], int offset, int len) {
if (len > 0) // let arraycopy report AIOOBE for len < 0
ensureCapacityInternal(count + len);
System.arraycopy(str, offset, value, count, len);
count += len;
return this;
}
public AbstractStringBuilder append(boolean b) {
if (b) {
ensureCapacityInternal(count + 4);
value[count++] = 't';
value[count++] = 'r';
value[count++] = 'u';
value[count++] = 'e';
} else {
ensureCapacityInternal(count + 5);
value[count++] = 'f';
value[count++] = 'a';
value[count++] = 'l';
value[count++] = 's';
value[count++] = 'e';
}
return this;
}
@Override
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
public AbstractStringBuilder append(int i) {
if (i == Integer.MIN_VALUE) {
append("-2147483648");
return this;
}
int appendedLength = (i < 0) ? Integer.stringSize(-i) + 1
: Integer.stringSize(i);
int spaceNeeded = count + appendedLength;
ensureCapacityInternal(spaceNeeded);
Integer.getChars(i, spaceNeeded, value);
count = spaceNeeded;
return this;
}
public AbstractStringBuilder append(long l) {
if (l == Long.MIN_VALUE) {
append("-9223372036854775808");
return this;
}
int appendedLength = (l < 0) ? Long.stringSize(-l) + 1
: Long.stringSize(l);
int spaceNeeded = count + appendedLength;
ensureCapacityInternal(spaceNeeded);
Long.getChars(l, spaceNeeded, value);
count = spaceNeeded;
return this;
}
public AbstractStringBuilder append(float f) {
FloatingDecimal.appendTo(f,this);
return this;
}
public AbstractStringBuilder append(double d) {
FloatingDecimal.appendTo(d,this);
return this;
}
public AbstractStringBuilder appendCodePoint(int codePoint) {
final int count = this.count;
if (Character.isBmpCodePoint(codePoint)) {
ensureCapacityInternal(count + 1);
value[count] = (char) codePoint;
this.count = count + 1;
} else if (Character.isValidCodePoint(codePoint)) {
ensureCapacityInternal(count + 2);
Character.toSurrogates(codePoint, value, count);
this.count = count + 2;
} else {
throw new IllegalArgumentException();
}
return this;
}
}
package java.lang;
import java.util.NoSuchElementException;
import java.util.PrimitiveIterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.IntConsumer;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;
public interface CharSequence {
int length();
char charAt(int index);
CharSequence subSequence(int start, int end);
public String toString();
public default IntStream chars() {
class CharIterator implements PrimitiveIterator.OfInt {
int cur = 0;
public boolean hasNext() {
return cur < length();
}
public int nextInt() {
if (hasNext()) {
return charAt(cur++);
} else {
throw new NoSuchElementException();
}
}
@Override
public void forEachRemaining(IntConsumer block) {
for (; cur < length(); cur++) {
block.accept(charAt(cur));
}
}
}
return StreamSupport.intStream(() ->
Spliterators.spliterator(
new CharIterator(),
length(),
Spliterator.ORDERED),
Spliterator.SUBSIZED | Spliterator.SIZED | Spliterator.ORDERED,
false);
}
public default IntStream codePoints() {
class CodePointIterator implements PrimitiveIterator.OfInt {
int cur = 0;
@Override
public void forEachRemaining(IntConsumer block) {
final int length = length();
int i = cur;
try {
while (i < length) {
char c1 = charAt(i++);
if (!Character.isHighSurrogate(c1) || i >= length) {
block.accept(c1);
} else {
char c2 = charAt(i);
if (Character.isLowSurrogate(c2)) {
i++;
block.accept(Character.toCodePoint(c1, c2));
} else {
block.accept(c1);
}
}
}
} finally {
cur = i;
}
}
public boolean hasNext() {
return cur < length();
}
public int nextInt() {
final int length = length();
if (cur >= length) {
throw new NoSuchElementException();
}
char c1 = charAt(cur++);
if (Character.isHighSurrogate(c1) && cur < length) {
char c2 = charAt(cur);
if (Character.isLowSurrogate(c2)) {
cur++;
return Character.toCodePoint(c1, c2);
}
}
return c1;
}
}
return StreamSupport.intStream(() ->
Spliterators.spliteratorUnknownSize(
new CodePointIterator(),
Spliterator.ORDERED),
Spliterator.ORDERED,
false);
}
}
package java.lang;
import java.io.IOException;
public interface Appendable {
Appendable append(CharSequence csq) throws IOException;
Appendable append(CharSequence csq, int start, int end) throws IOException;
Appendable append(char c) throws IOException;
}