简单看看这两个类 String和StringBuilder

简介: 简单看看这两个类 String和StringBuilder

以前在园子里面讨论这两个类的文章有很多很多,并且还拿出了很多的测试报告,在什么情况下,谁比谁快,在什么情况下,该用谁不该用谁等等这些,我这里就不比较了,我就简单看看他们里面的内部实现,那就先看看String吧。

一:String类

  说到String类,资料上都说是存在于堆上的一个不可CURD的一个不可变的字符集,当然看到这句话之后就想要看看是不是这样的,然后就

好奇的写了以下代码。

1     class Program
2     {
3         static void Main(string[] args)
4         {
5             string s = "123";
6         }
7     }
method private hidebysig static void Main(string[l arqs) cil managed
.entrypoint
// Code size  8(5x8)  
.maxstack 1
.locals init ([6] string s) IL_5555: nop
IL 5551:(1dstr  "123" 
IL 5556: stloc.5 IL 9557: ret
} // end of method program::Main  

从上面的IL中也就仅仅发现一个ldstr指令,看得出clr把string做成了基元类型,也就没看到它具体转换成了什么样的方法,是不是调用了string

的构造函数,这个也不清楚,也就不知道具体怎么把这个有序字符集放到堆中,不过办法还是有的,我们随便挑一个方法看看,比如简单一点的

substring,我们看看它的源代码。

public string Substring int startIndex) substring 
return this.Substring(startIndex, this.Length - startIndex);
DynamicallyInvokable,|TargetedPatching0ptOut("Performance critical to inline across NGen image boundaries"), SecuritySafeCrit public string Substring(int startIndex, int length)
return this.InternalsubStringWithChecks(startIndex, length, false);
[SecurityCritical]
internal string InternalsubstringWithChecks(int startIndex, int length, bool fAlwaysCopy)
if (startIndex<0){
thrownewArgumentdtOfRangeException("startIndex",Environment.GetResourceString("ArgumentOutofRange StartIndex"));
if (startIndex > this.Length){
thrownew ArgumentOuOfRangeException("startIndex",Environment.GetResourceString("ArgumentOutOfRange StartIndexLargerTh
if (length <@)
throw new ArgumentOutfRangeException("length",Environment.GetResourceString("ArgumentOutOfRange NegativeLength"));
if (startIndex > this.Length - length)
throw new ArgumentOut0 RangeException("length",Environment.GetResourceString("ArgumentOutOfRange_IndexLength"));
}
if(length == 0)
return string.Empty;
return this.InternalsubString(startIndex, length, fAlwaysCopy); 

然后我们找到了一个核心的方法,这个internalSubstring里面定义了两个指针ptr和ptr2,ptr则指向新申请的内存块的首地址,ptr2则指向原始

字符串的首地址,最后将ptr2的位置偏移startindex个位置,最后我们就找到了终极方法string.wstrcpy。

// string
[SecurityCritical]
private unsafe string InternalsubString(int startIndex, int length, bool fAlwaysCopy
if(startIndex == 0 && length == this.Length &&!fAlwaysCopy)
{
return this;
string text = string.FastAllocateString(length);  根据length申请合适的内存 
fixed (char* ptr = &text.m firstChar) 空间大小  
{
fixed (char* ptr2 = &this.m firstChar)
将ptr指向新申请的text字符串的
{
string.wstrcpy(ptr,ptr2 +(IntPtr)startIndex, length); 首地址。  
}
return text;
-}  这个startindex是将内存  ptr2指向了当前字符串的内存首地址  
地址向后偏移了starindex个位置 

在string.wstrcpy方法里面,虽然看的迷迷糊糊,不过还是能看到类似这样的偏移操作,一点一点的将smem地址上的字符赋值给dmem中,

确实也就说明了在堆上是有序的字符集。

// string
SecurityCritical]
Finternal unsafe static void wstrcpy(char* dmem, char* smem, int charCount)
if(charCount >0){
if((dmem & 2)!= 0){
*dmem = *smem;
dmem +=(IntPtr)2/2; smem +=(IntPtr)2/2; charcount--;
while(charCount >= 8){
*(int*)dmem =*(int*)smem;
*(int*)(dmem +(IntPtr)4 / 2) = *(int*)(smem + (IntPtr)4 / 2);*(int*)(dmem +(IntPtr)8 / 2) = *(int*)(smem + (IntPtr)8 / 2);*(int*)(dmem +(IntPtr)12 / 2) = *(int*)(smem + (IntPtr)12 / 2): dmem +=(IntPtr)16 /2; smem +=(IntPtr)16/2: charCount -= 8;}
if((charCount &4)!= 0){
*(int*)dmem =*(int*)smem;
*(int*)(dmem +(IntPtr)4 / 2) = *(int*)(smem + (IntPtr)4 / 2); dmem +=(IntPtr)8/2; smem +=(IntPtr)8/2;
if((charCount & 2) != 0){
*(int*)dmem = *(int*)smem; dmem +=(IntPtr)4/2;
smem t=(IntPtr)4/2;

同样在上面的源代码中来说,substring操作并没有对原始字符串进行修改,而是把截取的值放到新申请的内存地址空间中,这也就说明了字符

串是不可修改的说法,当然如果设计者真的要做到原位修改,那肯定也是能做到的,为了佐证下,我再举一个经常用到的concat方法,不过在

FastAllocateString方法中,并没有看到他的源代码,所以只能说根据length申请合适的空间。

public static string Concat(string stro, string str1){
if(string.IsNullOrEmpty(str0)){
if(string.IsNullOrEmpty(str1)){
return string.Empty
}
return str1;}
else{
if(string.IsNullOrEmpty(str1))
return str0;
根据str0.length+str2.length申请合适空间
int length = str0.Length;
string text=string.FastAllocateString(length + str1.Length): string.FillstringChecked(text,0,str0);
string.FillstringChecked(text,length,str1);
} return text;  赋值第一部分

所以结论出来了: 当你对字符串进行大量操作的时候,会产生很多的新的字符串,这些字符串会大量零碎的占据着堆空间,大多都是生存期较短的,所以一般都是在堆的第一代上,所以会对gc产生了比较大回收压力。

二:StringBuilder

   看这个类的话,还是看一下它的源代码,就抽一个Append吧,从下面这个截图中看出来几个有意思的地方。

<1> 原来StringBuilder里面维护的是一个m_ChunkChars的字符数组。

<2> 如果当前的字符串的length<2,会直接给chunkchars数组复制,length>2的时候看到的是刚才string类中经典的wstrcpy用法,而

     这个时候ptr指向的是chunkChars[chunkLength]的首地址,而不像string中申请新的内存空间,所以从这里看,比string大大的节省

    了内存空间。

public unsafe stringBuilder Append(string value
if (value != null)
{
char[] chunkChars = this.m chunkchars;  原来里面维护了一个 
int length = value.Length; int chunkLength = this.m_chunklength; m_ChunkChars的字符数组 int num = chunkLength + length; if (num < chunkChars.Length)
if (length <= 2){
if (length >@){
chunkChars[chunkLength] =value[0];
if (length >1)
chunkChars[chunkLength + 1] = value[1];}}
else
(
fixed(char* smem = value)
fixed (char* ptr = &chunkChars[chunkLength])
string.wstrcpy(ptr,smem,length);
}
this.m ChunkLength =num;}
else
{ 
this.AppendHelper(value):

好了,具体他们的性能比较我也不说了,大家看着他们的原理凑合着用吧,简单的看看也只能看到这了,再看就漏点了。



目录
相关文章
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
63 2
|
2月前
|
安全
String、StringBuffer、StringBuilder的区别
String 由 char[] 数组构成,使用了 final 修饰,对 String 进行改变时每次都会新生成一个 String 对象,然后把指针指向新的引用对象。 StringBuffer可变并且线程安全;有一定缓冲区容量,字符串大小没超过容量,不会重新分配新的容量,适合多线程操作字符串; StringBuiler可变并且线程不安全。速度比StringBuffer更快,适合单线程操作字符串。 操作少量字符数据用 String;单线程操作大量数据用 StringBuilder;多线程操作大量数据用 StringBuffer
|
3月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
73 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
3月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
64 2
|
3月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
33 1
|
3月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
71 4
|
3月前
|
canal 安全 索引
(StringBuffer和StringBuilder)以及回文串,字符串经典习题
(StringBuffer和StringBuilder)以及回文串,字符串经典习题
48 5
|
3月前
|
存储 安全 Java
【一步一步了解Java系列】:认识String类
【一步一步了解Java系列】:认识String类
33 2
|
3月前
|
存储 安全 Java
String、StringBuffer 和 StringBuilder 的区别
【10月更文挑战第21天】String、StringBuffer 和 StringBuilder 都有各自的特点和适用场景。了解它们之间的区别,可以帮助我们在编程中更合理地选择和使用这些类,从而提高程序的性能和质量。还可以结合具体的代码示例和实际应用场景,进一步深入分析它们的性能差异和使用技巧,使对它们的理解更加全面和深入。
61 0
|
3月前
|
C语言 C++
C++番外篇——string类的实现
C++番外篇——string类的实现
26 0