·天新网首页·加入收藏·设为首页·网站导航
数码笔记本手机摄像机相机MP3MP4GPS
硬件台式机网络服务器主板CPU硬盘显卡
办公投影打印传真
家电电视影院空调
游戏网游单机动漫
汽车新车购车试驾
下载驱动源码
学院开发设计
考试公务员高考考研
业界互联网通信探索
您现在的位置:天新网 > 软件开发 > .Net开发 > C#
C#基础知识梳理系列九:StringBuilder
http://www.21tx.com 2012年08月28日 博客园 solan3000

1 2 下一页

第一节 StringBuilder

在前面的章节中我们已经知道,字符串是由字符组成,由于字符串具有不可变性,所以每一次对字符串的变动都会重新分配内存、创建一个字符串对象、丢弃旧对象,在重新分配内存过程可能会导致垃圾回收,这一系列的操作,会大大损伤性能。为了解决这个问题,FCL提供了一个System.Text.StringBuilder类来构造管理字符串。就像它的名字一样,它是一个构造器,提供了对字符串的追加、移除和替换等功能,向StringBuilder对象追加字符串时,实际上在内部是转为追加字符。System.Text.StringBuilder类有几个重要的字段、属性和方法,我们一个一个来看。

第二节 字段和属性

(1) internal char[] m_ChunkChars

保存StringBuilder所管理着的字符串中的字符。系统默认初始化它的长度为16,当新追加进来的字符串长度与旧有字符串长度之和大于该字符数组容量时,新创建字符数组容量会增加到“2的(n+1)次幂”(假如当前字符数组容量为2的n次幂)。

(2) internal int m_ChunkLength;

字符数组m_ChunkChars内的实际字符个数,系统默认m_ChunkChars的容量是16。

(3) internal int m_ChunkOffset;

字符定位的偏移量

(4) internal StringBuilder m_ChunkPrevious;

内部的一个StringBuilder对象,追加的字符串长度和旧字符串长度之合大于字符数组m_ChunkChars的最大容量时,会根据当前的(this)StringBuilder创建一个新的StringBuilder对象,将m_ChunkPrevious指向新创建的StringBuilder对象。这个是关键。

(5) public int Length;

当前StringBuilder对象实际管理的字符串长度。Length = m_ChunkLength + m_ChunkOffset

(6) public int Capacity;

设置或获取字符数组m_ChunkChars的最大容量。

第三节 追加字符串时StringBuilder的内部工作

创建一个新的StringBuilder对象后,字符数组m_ChunkChars最大容量被初始化为16,向StringBuilder对象追加字符串,如果追加前后的字符串总长度小于等于16,则将新追加的字符串的字符复制到m_ChunkChars数组;如果追加前后的字符串总长度大于16,则先用新字符将当前m_ChunkChars填满,再以当前对象(this)为基础构造一个StringBuilder对象,并且将m_ChunkPrevious指向这个新创建的StringBuilder对象,然后将Capacity设置为2的(n+1)次幂32,重新初始化字符数组m_ChunkChars且容量为2的(n+1)次幂32(注意:这个不一定),然后将刚才剩余的字符复制到最新的字符数组m_ChunkChars。每一次追加字符串都会执行上面类似的步骤。下面我们来看一下这个过程,为了方便演示,我们在创建一个StringBuilder对象后,先设置容量为2。如下代码:

StringBuilder strBuilder = new StringBuilder(); 
strBuilder.Capacity = 2;

看一下初始化后的结果:

C#基础知识梳理系列九:StringBuilder

可以看到最大容量Capacity为2,由于未向其追加字符串,所以字符数组m_ChunkChars的元素为空,m_ChunkPrevious是null。

A)接着我们向其追加一个字符串”a”:

C#基础知识梳理系列九:StringBuilder

可以看到,新添加的字符a被放到了字符数组的0号位置,字符数组内元素个数为1。

B)接着追加一个字符b:

C#基础知识梳理系列九:StringBuilder

此时是将新字符b放到了字符数组1号位置,很显然字符数组的有效长度m_ChunkLength增加1后值为2,此时的m_chunkPrevious依然保持着null。

C)接着我们再添加一个字符c:

C#基础知识梳理系列九:StringBuilder

可以看到,strBuilder的字符容量Capacity已经变成2的(1+1)次幂4。因为原先长度为2的数组m_ChunkChars已经无法装载长度为3的字符串,所以要创新创建一个数组来扩容,但是这里使用旧有容量(值为2)创建的数组已经中以容纳新加进来的字符串 c ,所以m_ChunkChars数组依然被初始化为容量为2的数组。由于strBuilder内已经有3个字符,Length=m_ChunkLength+m_ChunkOffset,所以Length为3,最新的字符c已经放到了新数组m_ChunkChars的0号位。最主要的是字段m_ChunkPrevious已经不空null了,它已经指向截止到B)步骤的strBuilder对象,这个指向可以通过StringBuilder内部代码看的出来:

private void ExpandByABlock(int minBlockCharCount) 
        { 
            if ((minBlockCharCount + this.Length) > this.m_MaxCapacity) 
            { 
                throw new ArgumentOutOfRangeException("requiredLength", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity")); 
            } 
            int num = Math.Max(minBlockCharCount, Math.Min(this.Length, 0x1f40)); 
            this.m_ChunkPrevious = new StringBuilder(this); 
            this.m_ChunkOffset += this.m_ChunkLength; 
            this.m_ChunkLength = 0; 
            if ((this.m_ChunkOffset + num) < num) 
            { 
                this.m_ChunkChars = null; 
                throw new OutOfMemoryException(); 
            } 
            this.m_ChunkChars = new char[num]; 
        }

事实上初始化m_ChunkPrevious在前,创建新的字符数组m_ChunkChars在后,最后才是复制字符到数组m_ChunkChars中。

D)接着我们连续添加两个字符d和e:

C#基础知识梳理系列九:StringBuilder

在上一步骤C)的时候容量Capacity是4,字符数组还有一个空位置,所以当我们添加字符d时还可以用该数组,并不需要迁移对象和重建数组。但是在添加字符e的时候,由于总字符个数为5(abcde)已经超出了Capacity的4,所以此时会执行类似C)的步骤,最关键的两行代码:

this.m_ChunkPrevious = new StringBuilder(this); 
this.m_ChunkChars = new char[num];

需要说明,为了节省内存,StringBuilder内部并不一定是每次扩容m_ChunkChars真的按照2的(n+1)次幂进行计算,它是根据旧有字符串和新追加字符串的总长度和上一次容量的差来进行扩容:

int num = Math.Max(minBlockCharCount, Math.Min(this.Length, 0x1f40)); 
this.m_ChunkChars = new char[num];

归根结底,StringBuilder是在内部以字符数组m_ChunkChars为基础维护一个链表m_ChunkPreviou。如图:

上一篇: C# 温故而知新:Stream篇(—)
下一篇: C# 温故而知新:Stream篇(二)TextReader 和StreamReader

1 2 下一页

关于我们 | 联系我们 | 加入我们 | 广告服务 | 投诉意见 | 网站导航
Copyright © 2000-2011 21tx.com, All Rights Reserved.
晨新科技 版权所有 Created by TXSite.net