zoukankan      html  css  js  c++  java
  • StringBuilder的实现

    先看看MS给出的官方解释吧

    http://msdn.microsoft.com/zh-cn/library/system.text.stringbuilder(VS.80).aspx

    String 对象串联操作总是用现有字符串和新数据创建新的对象。StringBuilder 对象维护一个缓冲区,以便容纳新数据的串联。如果有足够的空间,新数据将被追加到缓冲区的末尾;否则,将分配一个新的、更大的缓冲区,原始缓冲区中的数据被复制到新的缓冲区,然后将新数据追加到新的缓冲区。

    String 或 StringBuilder 对象的串联操作的性能取决于内存分配的发生频率。

    给实现者的说明 此实现的默认容量是 16,默认的最大容量是 Int32.MaxValue。

          我只截取了最核心的一段,从这段我们就基本可以明白,StringBuilder的(后面直接简称sb,无节操的兄弟们,不要乱想,我也尽量不引导你们)实现,不过不看看源码,只看文档还是比较但蛋疼,理解不深。所以还是来看看其实现吧。

         

          二话不说,先上一段定义,ok(只取了几个构造函数)

    public sealed class StringBuilder : ISerializable
    {
        // Methods
        public StringBuilder();
        public StringBuilder(int capacity);
        public StringBuilder(int capacity, int maxCapacity);
    }
    那我们最常用的两个,一个是无参构造,一个是整形构造,查看其源码其实都是调用了下面这个构造函数,给出其源码
    public StringBuilder(int capacity, int maxCapacity)
    {
        this.m_currentThread = Thread.InternalGetCurrentThread();
        if (capacity > maxCapacity)
        {
            throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_Capacity"));
        }
        if (maxCapacity < 1)
        {
            throw new ArgumentOutOfRangeException("maxCapacity", Environment.GetResourceString("ArgumentOutOfRange_SmallMaxCapacity"));
        }
        if (capacity < 0)
        {
            throw new ArgumentOutOfRangeException("capacity", string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), new object[] { "capacity" }));
        }
        if (capacity == 0)
        {
            capacity = Math.Min(0x10, maxCapacity);
        }
        this.m_StringValue = string.GetStringForStringBuilder(string.Empty, capacity);
        this.m_MaxCapacity = maxCapacity;
    }
    两个参数一个是容量大小,另一个是最大容量。
    这个构造做了些什么
    1.  获取当前线程句柄
    2.  做一些必要性的验证和判断
    3.  调用了这个方法
    string.GetStringForStringBuilder(string.Empty, capacity);
     
    这个方法最终调用了这个方法
    internal static unsafe string GetStringForStringBuilder(string value, int startIndex, int length, int capacity)
    {
        string str = FastAllocateString(capacity);
        if (value.Length == 0)
        {
            str.SetLength(0);
            return str;
        }
        fixed (char* chRef = &str.m_firstChar)
        {
            fixed (char* chRef2 = &value.m_firstChar)
            {
                wstrcpy(chRef, chRef2 + startIndex, length);
            }
        }
        str.SetLength(length);
        return str;
    }
    其实就是分配了string控件,复制了一份,并返回了。

    然后我们看下append方法,很多重载,其实都是调用了

    public StringBuilder Append(string value)
    {
        if (value != null)
        {
            string stringValue = this.m_StringValue;
            IntPtr currentThread = Thread.InternalGetCurrentThread();
            if (this.m_currentThread != currentThread)
            {
                stringValue = string.GetStringForStringBuilder(stringValue, stringValue.Capacity);
            }
            int length = stringValue.Length;
            int requiredLength = length + value.Length;
            if (this.NeedsAllocation(stringValue, requiredLength))
            {
                string newString = this.GetNewString(stringValue, requiredLength);
                newString.AppendInPlace(value, length);
                this.ReplaceString(currentThread, newString);
            }
            else
            {
                stringValue.AppendInPlace(value, length);
                this.ReplaceString(currentThread, stringValue);
            }
        }
        return this;
    }

    这里做了什么呢,

    1. 获取新字符串的长度,
    2. NeedsAllocation方法判断当前容量是否可以容纳新串,如果不能,重新生成新字符串

    否则直接添加到旧的字符串中,实现方法如下

    internal unsafe void AppendInPlace(string value, int currentLength)
    {
        int length = value.Length;
        int index = currentLength + length;
        fixed (char* chRef = &this.m_firstChar)
        {
            fixed (char* chRef2 = &value.m_firstChar)
            {
                wstrcpy(chRef + currentLength, chRef2, length);
            }
            chRef[index] = '\0';
        }
        this.m_stringLength = index;

    }

    了解了上面的大概内容基本上也就知道了,sb的实现原理,正如msdn解释的一样。

    那么知道了这些有个屁用呢,msdn中也解释了,性能取决于内存分配的发生频率,既然知道了性能问题,那么以后我们写的时候是不是需要估计下字符串的大小呢。比如我们经常在后台拼接html字符串,sb的初始默认大小是16,每次容量不够,大小翻倍重新分配,(

    int capacity = currentString.Capacity * 2;创建新字符串的函数代码忘了贴,看了前面的实现基本就能猜到了,其中就这一句对我们比较有用

    )相信我们随便拼一个长度就要远大于16吧,这就导致了大量的内存分配发生,降低了效率,(这里就可以猜测string的连接操作应该是每次都进行内存分配)所以,sb初始预估大小还是很有用的。

  • 相关阅读:
    reservoid sample 蓄水池问题
    发展中的生命力——Leo鉴书69
    Nagios 监控mysqlserver具体实现过程
    OC可变參数的函数实现va_start、va_end、va_list的使用
    Qt移动应用开发(八):实现跨平台的QML和OpenGL混合渲染
    一个简单RPC框架是怎样炼成的(V)——引入传输层
    Spark编程指南V1.4.0(翻译)
    [Scala随用随学] —— sealed声明的作用
    《快学Scala》第八章 继承
    《快学Scala》第六章 对象 第七章 包和引入
  • 原文地址:https://www.cnblogs.com/superCow/p/3784457.html
Copyright © 2011-2022 走看看