美文网首页Java
《Java编程的逻辑》笔记30--剖析StringBuilder

《Java编程的逻辑》笔记30--剖析StringBuilder

作者: 天一方蓝 | 来源:发表于2019-07-25 17:16 被阅读3次
    剖析StringBuilder.png

    String ,StringBuilder和StringBuffer

    符串修改操作比较频繁,应该采用StringBuilder和StringBuffer类
    StringBuffer是线程安全的

    基本用法

    StringBuilder sb = new StringBuilder();
    sb.append("老马说编程");
    sb.append(",探索编程本质");
    System.out.println(sb.toString())
    输出为:
    老马说编程,探索编程本质
    

    基本实现原理

    • 内部组成和构造方法
      StringBuilder类也封装了一个字符数组

    与String不同
    它不是final的,可以修改
    字符数组中不一定所有位置都已经被使用,可“扩容”2n+2

    • 继承自AbstractStringBuilder
    public StringBuilder() {
        super(16);
    }
    
    public StringBuilder() {
        super(16);
    }
    

    默认长度为16

    • append的实现
    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;
    }
    

    append会直接拷贝字符到内部的字符数组中,如果字符数组长度不够,会进行扩展,实际使用的长度用count体现。具体来说,ensureCapacityInternal(count+len)会确保数组的长度足以容纳新添加的字符,str.getChars会拷贝新添加的字符到字符数组中,count+=len会增加实际使用的长度

    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }
    

    调用expandCapacity进行扩展

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

    扩展的逻辑是,分配一个足够长度的新数组2n+2,然后将原内容拷贝到这个新数组中,最后让内部的字符数组指向这个新数组

    • toString实现
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }
    

    基于内部数组新建了一个String,注意,这个String构造方法不会直接用value数组,而会新建一个,以保证String的不可变性

    • 更多构造方法和append方法
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }
    
    public StringBuilder(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }
    
    public StringBuilder append(boolean b)
    public StringBuilder append(char c)
    public StringBuilder append(double d) 
    public StringBuilder append(float f)
    public StringBuilder append(int i)
    public StringBuilder append(long lng)
    public StringBuilder append(char[] str)
    public StringBuilder append(char[] str, int offset, int len)
    public StringBuilder append(Object obj)
    public StringBuilder append(StringBuffer sb)
    public StringBuilder append(CharSequence s)
    public StringBuilder append(CharSequence s, int start, int end)
    

    append有多种重载形式,可以接受各种类型的参数,将它们转换为字符,添加进来

    其他修改方法

    • 插入
    StringBuilder sb = new StringBuilder();
    sb.append("老马说编程");
    sb.insert(0, "关注");
    sb.insert(sb.length(), "老马和你一起探索编程本质");
    sb.insert(7, ",");
    System.out.println(sb.toString());
    
    输出:关注老马说编程,老马和你一起探索编程本质
    
    public AbstractStringBuilder insert(int offset, String str) {
        if ((offset < 0) || (offset > length()))
            throw new StringIndexOutOfBoundsException(offset);
        if (str == null)
            str = "null";
        int len = str.length();
        ensureCapacityInternal(count + len);
        System.arraycopy(value, offset, value, offset + len, count - offset);
        str.getChars(value, offset);
        count += len;
        return this;
    }
    

    这个实现思路是,在确保有足够长度后,首先将原数组中offset开始的内容向后挪动n个位置,n为待插入字符串的长度,然后将待插入字符串拷贝进offset位置
    arraycopy的声明有个修饰符native,表示它的实现是通过Java本地接口实现的

    • 删除
    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;
    }
    
    • 替换
    StringBuilder sb = new StringBuilder();
    sb.append("老马说编程");
    sb.replace(3, 5, "Java");
    System.out.println(sb.toString());
    
    程序输出为:
    老马说Java
    
    • 翻转字符串
     public StringBuilder reverse()
    

    这个方法不只是简单的翻转数组中的char,对于增补字符,简单翻转后字符就无效了,这个方法能保证其字符依然有效,这是通过单独检查增补字符,进行二次翻转实现的。

    StringBuilder sb = new StringBuilder();
    sb.append("a");
    sb.appendCodePoint(0x2F81A);//增补字符:冬
    sb.append("b");
    sb.reverse();
    System.out.prrrintln(sb.toString()); 
    
    即使内含增补字符"冬",输出也是正确的,为:
    b冬a 
    
    • 长度方法
      数组长度不小于给定值
    public void ensureCapacity(int minimumCapacity)
    

    返回字符数组的长度

    public int capacity()
    

    返回数组实际使用的长度

    public int length()
    

    注意capacity()方法与length()方法的的区别,capacity返回的是value数组的长度,length返回的是实际使用的字符个数,是count实例变量的值

    • 直接修改长度
    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;
        }
    }
    
    • 缩减使用的空间
    public void trimToSize() {
        if (count < value.length) {
            value = Arrays.copyOf(value, count);
        }
    }
    
    • 与String类似的方法
      查取字符串
      indexOf
      substring
    • String的+和+=运算符
    String hello = "hello";
    hello+=",world";
    System.out.println(hello);
    

    编译器会转换为:

    StringBuilder hello = new StringBuilder("hello");
    hello.append(",world");
    System.out.println(hello.toString());
    

    循环内部,每一次+=操作,都会生成一个StringBuilder
    循环中建议使用StringBuilder的原因

    相关文章

      网友评论

        本文标题:《Java编程的逻辑》笔记30--剖析StringBuilder

        本文链接:https://www.haomeiwen.com/subject/dzshrctx.html