你不知道的Sring(系列三)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你不知道的Sring(系列三)相关的知识,希望对你有一定的参考价值。

AbstractStringBuilder

StringBuffer 和 StringBuilder 都继承了AbstractStringBuilder,很多方法都是直接super的父类AbstractStringBuilder的方法,所以我们分析下AbstractStringBuilder的源码.

1.成员变量

AbstractStringBuilder和String一样,在其内部都是以字符数组的形式实现的。也就是String,StringBuffer以及StringBuilder在其内部都是以字符数组的形式实现的。

char value[];  
int count;  

2.构造函数

AbstractStringBuilder的构造函数中传入的capacity是指容量,实际长度是以leng中的count表示的,注意区分容量和实际长度。

AbstractStringBuilder() {  
}  

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

3.容量和长度

public int length() {  
    return count;  
   }  
public int capacity() {  
    return value.length;  
   }  

4.AbstractStringBuilder的扩容

public void ensureCapacity(int minimumCapacity) {  
    if (minimumCapacity > value.length) {//如果传入的容量大于原来的容量就扩容  
        expandCapacity(minimumCapacity);  
    }  
  }  

 void expandCapacity(int minimumCapacity) {  
    int newCapacity = (value.length + 1) * 2;//首先默认扩容为  (原容量+1)*2  
        if (newCapacity < 0) {  
            newCapacity = Integer.MAX_VALUE;  
        } else if (minimumCapacity > newCapacity) {//如果传入的容量大于 默认扩容量,则传入容量为新容量  
        newCapacity = minimumCapacity;  
    }  
        value = Arrays.copyOf(value, newCapacity);  
    }  

5.字符串减少存储空间

如果实际长度小于容量,为了减少存储空间,就把容量缩小为刚好满足字符串长度。


public void trimToSize() {  
        if (count < value.length) {  
            value = Arrays.copyOf(value, count);  
        }  
    }  

6.void setLength(int newLength)


public void setLength(int newLength) {  
    if (newLength < 0)  
        throw new StringIndexOutOfBoundsException(newLength);  
    if (newLength > value.length)//设置长度大于容量时先扩容  
        expandCapacity(newLength);  

    if (count < newLength) {//设置长度大于原来长度时,后面部分补以空白  
        for (; count < newLength; count++)  
        value[count] = ‘\0‘;  
    } else {  
            count = newLength;  
        }  
    }  

7.char charAt(int index)

public char charAt(int index) {  
    if ((index < 0) || (index >= count))  
        throw new StringIndexOutOfBoundsException(index);  
    return value[index];  
    }  

8.void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)

一定要注意参数情况的考虑,主要是调用了System.arraycopy()的方法.


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

9.void setCharAt(int index, char ch)

方法的源码很简单,是直接替换该index的值,而不是后移之类的,因为是数组,不可变长度。

public void setCharAt(int index, char ch) {  
if ((index < 0) || (index >= count))  
    throw new StringIndexOutOfBoundsException(index);  
value[index] = ch;  
   }  

10.append(String str)

对于StringBuffer和StringBuilder重要的append()方法

public AbstractStringBuilder append(String str) {  
    if (str == null) str = "null";//若传入为null,则会在后面加上“null”  
        int len = str.length();  
    if (len == 0) return this;//传入长度为0,则返回本身  
    int newCount = count + len;  
    if (newCount > value.length)//传入后的长度大于容量就扩容  
        expandCapacity(newCount);  
    str.getChars(0, len, value, count);//用的是getChars()为value传值  
    count = newCount;  
    return this;  
    }  

11.append(boolean b)

这个源码有点意思

public AbstractStringBuilder append(boolean b) {  
       if (b) {  
           int newCount = count + 4;  
           if (newCount > value.length)  
               expandCapacity(newCount);  
           value[count++] = ‘t‘;  
           value[count++] = ‘r‘;  
           value[count++] = ‘u‘;  
           value[count++] = ‘e‘;  
       } else {  
           int newCount = count + 5;  
           if (newCount > value.length)  
               expandCapacity(newCount);  
           value[count++] = ‘f‘;  
           value[count++] = ‘a‘;  
           value[count++] = ‘l‘;  
           value[count++] = ‘s‘;  
           value[count++] = ‘e‘;  
       }  
return this;  
   }  

12.reverse()

这个是StringBuffer和StringBuilder常用到的方法,而String并没有这个功能
1、用一个循环反转序列;并在每次循环时判断调换的两个字符是否有一个字符在\uD800和\uDFFF之间,如果有则置hasSurrogate的值为true。

2、若hasSurrogate的值为true,则进入第二个循环,调用Character类的isLowSurrogate和isHighSurrogate方法判断,将反转的增补字符再次反转,以此可确保字符的正确性。

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

字符串的反转有多种实现方式,常用循环逆序、使用栈、递归、计算机的异或操作
栈有"后进先出(LIFO)"的特点。这一特点刚好用于反转字符串。

异或原理

使用异或操作能实现交换两个变量的值而不引入第三个变量。
两个数异或的结果再与其中一个数异或的结果是另外一个数

这涉及到了离散数学中的异或的性质:
1.交换律:A^B=B^A
2.结合律: A^(B^C)=(A^B)^C
3.恒等律:X^0=0
4.归零律:X^X=0
5.自反:A^B^B = A^0=A

根据以上性质:

A=A^B
B=A^B
A=A^B

eg
before:"Hello"
after: "olleH"

index: 0 1 2 3 4
char : H e l l o
ASCII: 72 101 108 108 111

以第0个字符和第4个字符交换为例:

交换前:
array[0]=1001000
array[4]=1101111

交换:
array[0]=array[0]^array[4]=0100111
array[4]=array[4]^array[0]=1001000
array[0]=array[0]^array[4]=1101111

交换后:
array[0]=1101111--->111-->o
array[4]=1001000--->72--->H
交换代码如下

  public static String strReverseWithXor(String string){
        if(string==null||string.length()==0)return string;
        char [] array =string.toCharArray();
        int length = string.length()-1;
        for(int i =0;i<length;i++,length--){
            array[i]^=array[length];
            array[length]^=array[i];
            array[i]^=array[length];
        }
        return new String(array);
    }

13. String toString()

这个只是一个抽象的方法,没有方法体。实现方式是由StringBuffer和StringBuilder实现,

public abstract String toString();

 StringBuffer:
public synchronized String toString() {
    if (toStringCache == null) {
        toStringCache = Arrays.copyOfRange(value, 0, count);
    }
    return new String(toStringCache, true);
}
 StringBuilder:
public String toString() {
    // Create a copy, don‘t share the array
    return new String(value, 0, count);
}

以上是关于你不知道的Sring(系列三)的主要内容,如果未能解决你的问题,请参考以下文章

你不知道的JavaScript学习笔记1——作用域

MySQL 系列你不知道的 视图触发器存储过程函数事务索引语句

你不知道的JS系列- 引擎怎么查找变量

你不知道的JS系列 ( 12 ) - 声明提升

你不知道的JS系列 ( 5 ) - 词法作用域

你不知道的JS系列 ( 7 ) - 欺骗词法作用域