1.4JDK源码阅读之AbstractStringBuilder
Posted 康子的自留地
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1.4JDK源码阅读之AbstractStringBuilder相关的知识,希望对你有一定的参考价值。
AbstractStringBuilder源码分析
简介
AbstractStringBuild 实现可修改的字符串,是StringBuffer和StringBuilder,这两个类的父类。
类声明
abstract class AbstractStringBuilder implements Appendable, CharSequence
默认访问控制修饰符,说明只能在包内使用,即只能在JDK内部使用
用abstract修饰类名说明AbstractStringBuild是一个抽象类,只能被继承,不能直接创建对象。它就一个抽象方法,toString方法。
实现了Appendable接口,Appendable能够被追加 char 序列和值的对象。如果某个类的实例打算接收来自 Formatter 的格式化输出,那么该类必须实现 Appendable 接口。 append(CharSequence csq) throws IOException:添加一个字符序列 append(CharSequence csq, int start, int end) throws IOException:添加一个字符序列的一部分 append(char c) throws IOException:添加一个字符
实现了Charsequence接口,代表该类,或其子类是一个字符序列。 length():需要实现该字符序列的长度 charAt(int index):可以取得下标为index的的字符 subSequence(int start, int end):可以得到该字符序列的一个子字符序列 toString():重写了父类Object的toString()
变量
/**
* 该值用于字符存储
* The value is used for character storage.
*/
char[] value;
/**
* 该值用于存储字符的实际数量,value.length = 100 实际存储字符数是30 则count = 30
* The count is the number of characters used.
*/
int count;
注:这里的value同String类中的value不同,String类中的value是final的不可被修改,这里的value是动态的,并且可提供给外部直接操作。
构造器
/**
* 这个无参数构造函数是序列化子类所必需的
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
/**
* 创建指定容量的AbstractStringBuilder
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
方法
1.length()
/**
* 返回已经存储字符序列的实际长度,即count的值。
* Returns the length (character count).
*
* @return the length of the sequence of characters currently
* represented by this object
*/
public int length() {
return count;
}
2.capacity()
/**
* 返回当前容量,在下一次添加元素的时候,可能会对数组进行扩容
* Returns the current capacity. The capacity is the amount of storage
* available for newly inserted characters, beyond which an allocation
* will occur.
*
* @return the current capacity
*/
public int capacity() {
return value.length;
}
3. ensureCapacity(int minimumCapacity)
/**
* 确保容量至少等于指定的最小值。如果当前容量小于参数,则分配一个新的具有更大容量的内部数组。
* 新容量是最小容量参数中较大的一个。
* 是原来容量的两倍,加上2。
* 如果minimumCapacity参数是非正的,则此方法不采取任何操作,只返回。
*
* Ensures that the capacity is at least equal to the specified minimum.
* If the current capacity is less than the argument, then a new internal
* array is allocated with greater capacity. The new capacity is the
* larger of:
* <ul>
* <li>The <code>minimumCapacity</code> argument.
* <li>Twice the old capacity, plus <code>2</code>.
* </ul>
* If the <code>minimumCapacity</code> argument is nonpositive, this
* method takes no action and simply returns.
*
* @param minimumCapacity the minimum desired capacity.
*/
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0)
ensureCapacityInternal(minimumCapacity);
}
/**
* 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);
}
这里提个问题:上面 expandCapacity(int minimumCapacity) 方法中 为何newCapacity后面还需要判断是否小于零?文末会公布答案。
4. trimToSize()
/**
* 减少字符序列的使用空间,比如申请了100字符长度的空间,但是现在只用了60个,
* 那剩下的40个无用的空间放在那里占内存,可以调用此方法释放掉未用到的内存。
* 原理很简单,只申请一个count大小的数组把原数组中的内容复制到新数组中,
* 原来的数组由于没有被任何引用所指向,之后会被gc回收。
*
* Attempts to reduce storage used for the character sequence.
* If the buffer is larger than necessary to hold its current sequence of
* characters, then it may be resized to become more space efficient.
* Calling this method may, but is not required to, affect the value
* returned by a subsequent call to the {@link #capacity()} method.
*/
public void trimToSize() {
if (count < value.length) {
value = Arrays.copyOf(value, count);
}
}
5. setLength(int newLength)
/**
* 用空字符'\0'填充未使用的空间。首先对数组进行扩容,然后将剩余未使用的空间全部填充为'\0'字符。
* newLength参数必须大于或等于0。
*
* Sets the length of the character sequence.
* The sequence is changed to a new character sequence
* whose length is specified by the argument. For every nonnegative
* index <i>k</i> less than <code>newLength</code>, the character at
* index <i>k</i> in the new character sequence is the same as the
* character at index <i>k</i> in the old sequence if <i>k</i> is less
* than the length of the old character sequence; otherwise, it is the
* null character <code>'\u0000'</code>.
*
* In other words, if the <code>newLength</code> argument is less than
* the current length, the length is changed to the specified length.
* <p>
* If the <code>newLength</code> argument is greater than or equal
* to the current length, sufficient null characters
* (<code>'\u0000'</code>) are appended so that
* length becomes the <code>newLength</code> argument.
* <p>
* The <code>newLength</code> argument must be greater than or equal
* to <code>0</code>.
*
* @param newLength the new length
* @throws IndexOutOfBoundsException if the
* <code>newLength</code> argument is negative.
*/
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;
}
}
6. charAt(int index)
/**
* 获取字符序列中指定位置的字符,范围为0到count,超出范围抛StringIndexOutOfBoundsException异常。
*/
public char charAt(int index) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
return value[index];
}
7. codePointAt(int index)
/**
* 获取字符序列中指定位置的字符,所对应的代码点,即ascii码。
*/
public int codePointAt(int index) {
if ((index < 0) || (index >= count)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointAt(value, index);
}
8.codePointBefore(int index)
/**
* 获取字符序列中指定位置的前一个位置的字符,所对应的代码点。
*/
public int codePointBefore(int index) {
int i = index - 1;
if ((i < 0) || (i >= count)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointBefore(value, index);
}
9. codePointCount(int beginIndex, int endIndex)
/**
* 获取字符串代码点个数,是实际上的字符个数
*/
public int codePointCount(int beginIndex, int endIndex) {
if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) {
throw new IndexOutOfBoundsException();
}
return Character.codePointCountImpl(value, beginIndex, endIndex-beginIndex);
}
10. offsetByCodePoints(int index, int codePointOffset)
/**
* 返回此字符序列中从给定的index处偏移codePointOffset个代码点的索引。
*/
public int offsetByCodePoints(int index, int codePointOffset) {
if (index < 0 || index > count) {
throw new IndexOutOfBoundsException();
}
return Character.offsetByCodePointsImpl(value, 0, count,
index, codePointOffset);
}
不清楚代码点的可以查看这一段,清楚的大佬请往下划拉 码点和代码单元这两个概念还是在《Java核心技术 卷一》发现的有兴趣可以看看这本书。 代码点: 代码点(code point)是指编码字符集中,字符所对应的数字。有效范围从U+0000到U+10FFFF。其中U+0000到U+FFFF为基本字符,U+10000到U+10FFFF为增补字符。 代码单元: 代码单元(code unit):对代码点进行编码得到的1或2个16位序列。其中基本字符的代码点直接用一个相同值的代码单元表示,增补字符的代码点用两个代码单元的进行编码,这个范围内没有数字用于表示字符,因此程序可以识别出当前字符是单单元的基本字符,还是双单元的增补字符。 代码点与代码单元: 一个代码单元为16位二进制,一个代码点为一个或两个16位二进制。即一个代码点可表示为一个代码单元或两个代码单元。
11. getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
/**
* 将字符序列中指定区间srcBegin到srcEnd内的字符拷贝到dst字符数组中从dstBegin开始往后的位置中。
*/
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);
}
12. setCharAt(int index, char ch)
/**
* 设置字符序列中指定索引index位置的字符为ch
*/
public void setCharAt(int index, char ch) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
value[index] = ch;
}
13. append(……)
AbstractStringBuilder类中有很多append方法,作用是在原字符序列后添加给定的对象或元素所对应的字符序列,其方法原理类似。 1.首先判断所传参数是否为null,如果为null则调用append("null")。 2如果不为null则进行扩容操作,最小值为count+len,这一步可能增加容量也可能不增加,当count+len小于或等于capacity就不用进行扩容。 3.然后再将参数的字符串序列添加到value中。 4.最后返回this,注意这里返回的是this,也就意味者,可以在一条语句中多次调用append方法,即方法调用链。asb.append("asd").append("fgh");
14.delete(int start, int end)
/**
* 删除字符序列指定区间的内容。这个操作不改变原序列的容量。
*/
public AbstractStringBuilder delete(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end);
count -= len;
}
return this;
}
15. appendCodePoint(int codePoint)
/**
* 在结尾添加代码点
*/
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;
}
16. deleteCharAt(int index)
/**
* 删除字符序列中指定索引index位置的字符
*/
public AbstractStringBuilder deleteCharAt(int index) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
System.arraycopy(value, index+1, value, index, count-index-1);
count--;
return this;
}
17. replace(int start, int end, String str)
/**
* 将原字符序列指定区间start到end区间内的内容替换为str,替换过程中序列长度会改变,所以需要进行扩容和改就count的操作
*/
public AbstractStringBuilder replace(int start, int end, String str) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (start > count)
throw new StringIndexOutOfBoundsException("start > length()");
if (start > end)
throw new StringIndexOutOfBoundsException("start > end");
if (end > count)
end = count;
int len = str.length();
int newCount = count + len - (end - start);
ensureCapacityInternal(newCount);
System.arraycopy(value, end, value, start + len, count - end);
str.getChars(value, start);
count = newCount;
return this;
}
18. substring(……)
public String substring(int start) {
return substring(start, count);
}
/**
* 切割原字符序列指定区间start到end内的内容,返回字符串
*/
public String substring(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
throw new StringIndexOutOfBoundsException(end);
if (start > end)
throw new StringIndexOutOfBoundsException(end - start);
return new String(value, start, end - start);
}
19. subSequence(int start, int end)
/**
* 切割原字符序列指定区间start到end内的内容,返回CharSequence
*/
public CharSequence subSequence(int start, int end) {
return substring(start, end);
}
20. insert(……)
insert 等方法作用是将给定定对象所对应的字符串插入到原序列的指定位置,同 append 类似但是 append 是末尾添加 insert 是指定位置插入元素
21. indexOf(……)
/**
* 查询给定字符串在原字符序列中第一次出现的位置,通过 String 的 indexOf 实现
*/
public int indexOf(String str) {
return indexOf(str, 0);
}
public int indexOf(String str, int fromIndex) {
return String.indexOf(value, 0, count,
str.toCharArray(), 0, str.length(), fromIndex);
}
21. lastIndexOf(……)
/**
* 查询给定字符串在原字符序列中最后一次出现的位置,通过 String 的 lastIndexOf 实现
*/
public int lastIndexOf(String str) {
return lastIndexOf(str, count);
}
public int lastIndexOf(String str, int fromIndex) {
return String.lastIndexOf(value, 0, count,
str.toCharArray(), 0, str.length(), fromIndex);
}
22. reverse()
/**
* 该方法用于将字符序列反转
*
*/
public AbstractStringBuilder reverse() {
boolean hasSurrogate = false;
int n = count - 1;
for (int j = (n-1) >> 1; j >= 0; --j) {
char temp = value[j];
char temp2 = value[n - j];
if (!hasSurrogate) {
hasSurrogate = (temp >= Character.MIN_SURROGATE && temp <= Character.MAX_SURROGATE)
|| (temp2 >= Character.MIN_SURROGATE && temp2 <= Character.MAX_SURROGATE);
}
value[j] = temp2;
value[n - j] = temp;
}
if (hasSurrogate) {
// Reverse back all valid surrogate pairs
for (int i = 0; i < count - 1; i++) {
char c2 = value[i];
if (Character.isLowSurrogate(c2)) {
char c1 = value[i + 1];
if (Character.isHighSurrogate(c1)) {
value[i++] = c1;
value[i] = c2;
}
}
}
}
return this;
}
hasSurrogate 是用来标识 字符序列中是否包含surrogates pair Surrogate Pair是UTF-16中用于扩展字符而使用的编码方式,是一种采用四个字节(两个UTF-16编码)来表示一个字符。 char在java中是16位的,刚好是一个UTF-16编码。而字符串中可能含有Surrogate Pair,但他们是一个单一完整的字符, 只不过是用两个char来表示而已,因此在反转字符串的过程中Surrogate Pairs 是不应该被反转的。
23. toString()
/**
* 返回此序列中的数据的字符串
*
* 这个抽象类中唯一的一个抽象方法,需要子类去实现
*/
public abstract String toString();
如果你喜欢这篇文章,点好看,点转发。
Life is fantastic!明天见(。・ω・。)ノ♡
以上是关于1.4JDK源码阅读之AbstractStringBuilder的主要内容,如果未能解决你的问题,请参考以下文章