0
点赞
收藏
分享

微信扫一扫

深入学习java源码之StringBuilder.append()与StringBuilder.appendCodePoint()


深入学习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<len){
sb.append(Character.toUpperCase(param.charAt(i)));
}
}else{
sb.append(c);
}
}
return sb.toString();
}

 

Modifier and Type

Method and Description

​StringBuilder​

​append(boolean b)​

将 ​​boolean​​参数的字符串表示附加到序列中。

​StringBuilder​

​append(char c)​

将 ​​char​​参数的字符串表示附加到此序列。

​StringBuilder​

​append(char[] str)​

将 ​​char​​数组参数的字符串表示追加到此序列。

​StringBuilder​

​append(char[] str, int offset, int len)​

将 ​​char​​数组参数的子阵列的字符串表示附加到此序列。

​StringBuilder​

​append(CharSequence​

将指定的字符序列追加到此 Appendable 。

​StringBuilder​

​append(CharSequence​

追加指定的序列 ​​CharSequence​​的序列。

​StringBuilder​

​append(double d)​

将 ​​double​​参数的字符串表示法附加到此序列。

​StringBuilder​

​append(float f)​

将 ​​float​​参数的字符串表示附加到此序列。

​StringBuilder​

​append(int i)​

将 ​​int​​参数的字符串表示法附加到此序列。

​StringBuilder​

​append(long lng)​

将 ​​long​​参数的字符串表示法附加到此序列。

​StringBuilder​

​append(Object​

追加 ​​Object​​​参数的字符串 ​​Object​​形式。

​StringBuilder​

​append(String​

将指定的字符串附加到此字符序列。

​StringBuilder​

​append(StringBuffer​

将指定 ​​StringBuffer​​这个序列。

​StringBuilder​

​appendCodePoint(int codePoint)​

将 ​​codePoint​​参数的字符串表示附加到此序列。

​int​

​capacity()​

返回当前容量。

​char​

​charAt(int index)​

返回 ​​char​​在指定索引在这个序列值。

​void​

​ensureCapacity(int minimumCapacity)​

确保容量至少等于规定的最小值。

​void​

​getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)​

字符从该序列复制到目标字符数组 ​​dst​​ 。

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

 

 

 

举报

相关推荐

0 条评论