Java中的String、StringBuilder、StringBuffer 的区别

String

  • String类使用了final修饰,所以 String 类不能被继承。
  • String为不可变字符串,平常我们修改一个 String 字符串,实际都是创建一个新的字符串。
String a = "aaa";
String b = new String("aaa");
String c =  a + "";
String d = "aaa";
String e = a;

System.out.println(a == b);
System.out.println(a == c);
System.out.println(a == d);
System.out.println(a == e);

 没想到吧,a + 空字符串 的 c 不等于 a。

从中我们可以看出 字符串变量a + 空字符串 虽然值与 a 一样,但是分配了两块内存了。

那 "aaa" + "" 呢?其实编辑器对两个相加的字符串常量做了优化,"aaa" + "" 相当于 "aaa"。

a == d 是因为创建 d 的时候会先去字符串常量池中查找,如果找到就直接返回,否则就创建一个新的字符串。因为这时a已经创建好了,所以直接返回 a 的引用。

String a = "aaa";
String b = "bbb";

String c = a + b;

字符串 a + b ,会先创建一个 StringBuilder 对象,然后再调用append()方法进行拼接,最后通过toString()方法取得结果。

过程就如:

String a = "aaa";
String b = "bbb";
StringBuilder builder = new StringBuilder();
builder.append(a);
builder.append(b);
String c =  builder.toString();

StringBuilder、StringBuffer

  •  StringBuilder和StringBuffer都是被final修饰,不能被继承。
  • 两者都属于可变字符串,字符串改变不会重新创建一个新的字符串。
  • StringBuffer 是线程安全的,StringBuilder为非线程安全。
  • StringBuffer 使用 synchronized,会进行同步,所以效率没有StringBuilder快。

常用方法:append,insert 方法

先来看StringBuilder 的 append 方法:

append 方法是用StringBuilder 的父类 AbstractStringBuilder 类的 append 方法

AbstractStringBuilder 类的两个全局变量,value 是存储字符的数组,count 是存储字符的个数,也就是字符串的长度。

/**
     * The value is used for character storage.
     * 存储字符的数组
     */
    char[] value;

    /**
     * The count is the number of characters used.
     * 使用的字符数
     */
    int count;

append 方法源码:

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

首先判断是否为null,为null 就拼接 "null" 字符串,appendNull 方法源码:

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

不为 null 接下来就执行 ensureCapacityInternal 方法:判断存储字符的数组是否需要扩容,参数 count + len ,count:原字符长 度 + len:本次拼接的字符长度。

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity));
    }
}

minimumCapacity 拼接后字符串到达的长度,与 value 存储字符数组比较,如果字符数组长度小于minimumCapacity,数组就进行扩容。Arrays.copyOf(旧数组, 新数组长度);

扩容多少呢?请看 newCapacity(minimumCapacity) 方法源码:

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

int newCapacity = (value.length << 1) + 2;其实就是 (原字符数组长度 * 2 )+ 2。

(value.length << 1) 就原字符数组长度 的 二进制 左移 一 位 。

就拿10来说吧,10的二进制是 1010。

十进制转二进制的图,画的有点丑,将就着看吧。

二进制左移一位 ,1010 左移一位就是 10100 ,后面补 0.

二进制再转成十进制 :

0 + 0 + 4 + 0 + 16 = 20,10100 的十进制 就是 20。

所以说(value.length << 1) + 2;其实就是 (原字符数组长度 * 2 )+ 2。

if (newCapacity - minCapacity < 0) {
    newCapacity = minCapacity;
}

要扩容的长度 是否大于 拼接后字符串到达的长度,如果小于 就取 拼接后字符串到达的长度 作为要扩容的长度。

上面都完成后 ,接下来调用String 类的 getChars 方法 将此字符串中的字符复制到目标字符数组中。

str.getChars(0, len, value, count);
    public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

StringBuffer 的 append 方法与 StringBuilder 的差不多就不说了。

insert 方法:

append 区别 就是 insert 可以把字符串插入到指定位置,append 是在尾部追加。

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

String、StringBuilder、StringBuffer 的区别

 String是不可变字符串,StringBuilder、StringBuffer 为可变字符串,String 字符串每次修改都会产生一个新的字符串,StringBuilder、StringBuffer 修改不会产生新的字符串,修改的都是同一个字符串,所以效率会比 String 的效率高。

StringBuffer 是线程安全的,会进行同步,StringBuilder 是非线程安全 ,所以 StringBuilder 比 StringBuffer 的效率高。

下面写一个简单的例子:

StringBuffer :

String a = "aaa";
StringBuffer buffer = new StringBuffer();
Long startTs = System.currentTimeMillis();

for (int i = 0; i < 100000; i++) {
    buffer.append(a);
}
System.out.println(System.currentTimeMillis() - startTs);
System.out.println(startTs);
System.out.println(System.currentTimeMillis());

StringBuilder :

String a = "aaa";
StringBuilder builder = new StringBuilder();
Long startTs = System.currentTimeMillis();

for (int i = 0; i < 100000; i++) {
    builder.append(a);
}
System.out.println(System.currentTimeMillis() - startTs);
System.out.println(startTs);
System.out.println(System.currentTimeMillis());

String :

String a = "aaa";
String b = "bbb";
Long startTs = System.currentTimeMillis();

for (int i = 0; i < 100000; i++) {
    b += a;
}
System.out.println(System.currentTimeMillis() - startTs);
System.out.println(startTs);
System.out.println(System.currentTimeMillis());

从中可以看出 StringBuilder、StringBuffer 比 String 的效率高很多。

StringBuilder 与 StringBuffer 的差别不是很大。

三者之间的效率:StringBuilder > StringBuffer > String。

所以经常操作的字符串尽量用 StringBuilder 和 StringBuffer,如果不需要线程安全 尽量使用 StringBuilder。

 

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值