String StringBuilder StringBuffer详细分析(含源码分析)

首先在这里列出三者的特点。

  • String:不可变的字符序列,效率低。
  • StringBuilder:可变,线程不安全,效率高。
  • StringBuffer:可变,线程不安全,效率较高。

三者的效率:StringBuilder > StringBuffer > String

String

首先我们看看String的内部结构。

public final class String

利用final关键字修饰类,使得String不得被继承。

private final char value[];

维护了一个private final修饰的字符数组,使得value的指向不可变。
这是String类不可变的主要原因。

  1. private使得value数组不能在类外访问,也就无法修改其中某个字符(虽然利用反射可以修改)
  2. final使得value指向不能改变,使得所有对字符串修改的操作,都会重新创建String对象,重新设置value数组。

这也就保证了String的安全性。
有同学可能会思考,既然不可变会影响效率,为什么还要这样设计呢?
想想看,如果说String类型的变量s存在某个容器中,这时,在其他地方对s进行更改,我们其实是并不希望容器中的s也发生变化的。通常使用字符串的方法,如replace,substring等,我们都是不需要他本身发生改变的。

StringBuilder

因为String在发生变化的时候都会创建对象,有时候就特别影响效率。
比如在循环中使用+操作。
比如以下代码,大家可以去测试一下。速度差了相当多。

    public static void main(String[] args) {
        String s1 = new String();
        StringBuilder s2 = new StringBuilder();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 40000; i++) {
            s1 += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("String: " + (end - start));
        start = System.currentTimeMillis();
        for (int i = 0; i < 40000; i++) {
            s2.append(i);
        }
        end = System.currentTimeMillis();
        System.out.println("StringBuilder: " + (end - start));
    }
String: 4600ms
StringBuilder: 1ms

本质上就是因为StringBuilder底层的字符数组是char[] value,没有关键词修饰,所以可以直接修改。
所以说当字符串需要进行修改操作时,我们可以创建一个StringBuilder来帮助操作。
常用的操作有:

  • 增:append()
  • 删:delete()
  • 改:setCharAt(int n, char ch) replace(int start, int end, String str)
  • 插:insert(int offset, xxx)
  • 反转:reverse()

剩下的操作和String一样。

StringBuffer

与StringBuilder不同的是保证线程安全, 使用synchronized关键字来保证的线程安全。
还有一点,看源码发现StringBuffer多了一个变量。

private transient char[] toStringCache;
//toString返回的最后一个值的缓存。每当修改StringBuffer时清除。

它的存在是为了让StringBuffer的toString操作更快。

    @Override
    public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }

这是StringBuffer的toString方法,另外,在对StringBuffer对象修改时都会加一句**toStringCache = null;**所以说toStringCache的存在是让连续的toString()操作变快,具体为什么快呢?看它调用的构造参数

    String(char[] value, boolean share) {
        // assert share : "unshared not supported";
        this.value = value;
    }

是直接赋值将toStringCache赋值给String的value数组。很巧妙的方法,用空间换取了时间。对比一下StringBuilder调用的构造方法吧。

    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

显然这块速度会慢不少。

结束啦,谢谢阅读。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值