• java中String、StringBuffer、StringBuilder的区别


    Java里面提供了String,StringBuffer和StringBuilder三个类来封装字符串

    0. 不当用法

    String result = "";  
    for (String s : hugeArray) {  
        result = result + s;  
    }  

    不要使用String类的"+"来进行频繁的拼接,因为那样的性能极差的,应该使用StringBuffer或StringBuilder类,这在Java的优化上是一条比较重要的原则

    1. String类封装的字符串是不可变的

    字符串是由若干个字符线性排列组成的,String类的关键源码如下

    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
       private final char value[];//final类型char数组
    //省略其他代码……
    ……
    }

    因为有“final”修饰符,所以可以String类封装的字符串是不可变的。那么增删改是怎么实现的呢?下面是字符串截取的关键源码

    public String substring(int beginIndex) {
            if (beginIndex < 0) {
                throw new StringIndexOutOfBoundsException(beginIndex);
            }
            int subLen = value.length - beginIndex;
            if (subLen < 0) {
                throw new StringIndexOutOfBoundsException(subLen);
            }
           //当对原来的字符串进行截取的时候(beginIndex >0),返回的结果是新建的对象
            return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
        }

    可以看到截取源码是生成了一个新的String对象返回的。

    所以,在对String类型的字符串进行大量“插入”和“删除”操作时会产生大量的临时变量。

    既然String类封装的字符串是不可变的,那么是否应该有一个类来封装可变数组。答案是有的,StringBuilder与StringBuffer封装类都是可变的。

    2. 如何做到封装数组可变

    StringBuilder与StringBuffer都继承自AbstractStringBuilder抽象类,在AbstractStringBuilder中也是使用字符数组保存字符串,其关键代码如下

    abstract class AbstractStringBuilder implements Appendable, CharSequence {
        /**
         * The value is used for character storage.
         */
        char[] value;//一个char类型的数组,非final类型,这一点与String类不同
    
        /**
         * This no-arg constructor is necessary for serialization of subclasses.
         */
        AbstractStringBuilder() {
        }
    
        /**
         * Creates an AbstractStringBuilder of the specified capacity.
         */
        AbstractStringBuilder(int capacity) {
            value = new char[capacity];//构建了长度为capacity大小的数组
        }
    
    //其他代码省略……
    ……
    }

    StringBuffer类实现代码如下

    public final class StringBuffer
        extends AbstractStringBuilder
        implements java.io.Serializable, CharSequence
    {
       /**
         * Constructs a string buffer with no characters in it and an
         * initial capacity of 16 characters.
         */
        public StringBuffer() {
            super(16);//创建一个默认大小为16的char型数组
        }
    
        /**
         * Constructs a string buffer with no characters in it and
         * the specified initial capacity.
         *
         * @param      capacity  the initial capacity.
         * @exception  NegativeArraySizeException  if the {@code capacity}
         *               argument is less than {@code 0}.
         */
        public StringBuffer(int capacity) {
            super(capacity);//自定义创建大小为capacity的char型数组
        }
    //省略其他代码……
    ……

    可以看到StringBuffer创建字符串对象默认的大小是16,当然也可以传入数组大小。

    下面列下StringBuffer的用法

    public class Test{
      public static void main(String args[]){
        StringBuffer sBuffer = new StringBuffer("hello");
        sBuffer.append(" ");
        sBuffer.append("world");
        System.out.println(sBuffer); 
      }
    }

    输出

    hello world

    下面看源码分析下append函数,来看下如何做到长度可变

    public AbstractStringBuilder append(String str) {
            if (str == null)
                return appendNull();
            int len = str.length();
           //调用下面的ensureCapacityInternal方法
            ensureCapacityInternal(count + len);
            str.getChars(0, len, value, count);
            count += len;
            return this;
        }
    
    private void ensureCapacityInternal(int minimumCapacity) {
            // overflow-conscious code
            if (minimumCapacity - value.length > 0)
               //调用下面的expandCapacity方法实现“扩容”特性
                expandCapacity(minimumCapacity);
        }
    
       /**
         * This implements the expansion semantics of ensureCapacity with no
         * size check or synchronization.
         */
        void expandCapacity(int minimumCapacity) {
           //“扩展”的数组长度是按“扩展”前数组长度的2倍再加上2 byte的规则来扩展
            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返回的新的char[]对象,从而达到“扩容”的特性
            value = Arrays.copyOf(value, newCapacity);
        }

    可以看到空间扩展室友copyOf函数实现:

    public static char[] copyOf(char[] original, int newLength) {
            //创建长度为newLength的char数组,也就是“扩容”后的char 数组,并作为返回值
            char[] copy = new char[newLength];
            System.arraycopy(original, 0, copy, 0,
                             Math.min(original.length, newLength));
            return copy;//返回“扩容”后的数组变量
        }

    3. StringBuilder与StringBuffer 区别

    AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法,StringBuilder和StringBuffer的方法实现基本上一致,不同的是StringBuffer类的方法前多了个synchronized关键字,即StringBuffer是线程安全的。

    public synchronized StringBuffer reverse() {
        super.reverse();
        return this;
    }
    
    public int indexOf(String str) {
        return indexOf(str, 0);        //存在 public synchronized int indexOf(String str, int fromIndex) 方法
    }

    (1)append,insert,delete方法最根本上都是调用System.arraycopy()这个方法来达到目的

    (2)substring(int, int)方法是通过重新new String(value, start, end - start)的方式来达到目的。因此,在执行substring操作时,StringBuilder和String基本上没什么区别。

    4. 使用场景

    如果是多线程环境下涉及到共享变量的插入和删除操作,StringBuffer则是首选。如果是非多线程操作并且有大量的字符串拼接,插入,删除操作则StringBuilder是首选。毕竟String类是通过创建临时变量来实现字符串拼接的,耗内存还效率不高,怎么说StringBuilder是通过JNI方式实现终极操作的。

    5. 总结

    • String类型的字符串对象是不可变的,一旦String对象创建后,包含在这个对象中的字符系列是不可以改变的,直到这个对象被销毁。
    • StringBuilder和StringBuffer类型的字符串是可变的,不同的是StringBuffer类型的是线程安全的,而StringBuilder不是线程安全的
    • 如果是多线程环境下涉及到共享变量的插入和删除操作,StringBuffer则是首选。如果是非多线程操作并且有大量的字符串拼接,插入,删除操作则StringBuilder是首选。
    • 如果要操作少量的数据,用String;单线程操作大量数据,用StringBuilder;多线程操作大量数据,用StringBuffer。
  • 相关阅读:
    今年的第几天?
    特殊乘法
    abc
    求最大最小数
    二叉树遍历
    球的半径和体积
    成绩排序
    OC学习篇之---类的定义
    OC学习篇之---第一个程序HelloWorld
    OC学习篇之---类的初始化方法和点语法的使用
  • 原文地址:https://www.cnblogs.com/kaituorensheng/p/8168024.html
Copyright © 2020-2023  润新知