String StringBuilder
这个,我不打算再整理了,直接将我自己在coding民工群里面的聊天记录粘贴过来了。
顾伟伟(979605089) 11:16:25
魏在吗? 帮我看一下,我对于string与stringbuilder的理解
string是常量,不可变。stringbuilder可变。自己认真理解这里的可变与不可变。楼上一个小白来 了句string s="0";s="1"来说明string是可变的。完全不懂此处什么常量的概念。楼上有一个娃举得例子是哪个3个string,一个string的那个,其实他理解对了,只是别的地方知识欠缺了,所以误人子弟了。用下面这个例子说吧。
string s="aa"; s +="bb"; 则内存中存在了aa bb aabb 三个
stringbuilder ss="aa"; ss.Append("bb")。则内存中只有两个 "bb" "aabb" 此处少了一个"aa",其实是因为stringbuilder是可变的,"aa"变成了"aabb",而不是重新分配新空间。
楼主其实问的是为什么是stringbuilder比string好,还是存在string。因为stringbuilder是引用类型的,一开始分配的时候需要比较多的空间,而string是值类型的。这个有点struct与class的利害比较了。如果不是频繁拼接的情况下,。string的性能明显好于stringbuilder,所以楼上有一个人说的很对,如果频繁拼接就用stringbuilder,如果是展现那么用string。
魏文元(3518499) 11:19:13
String 用于少量字符处理。
StringBuilder 用于大量字符处理。
注:处理是指追加等操作,不是机械化的执行。
顾伟伟(979605089) 11:19:39
那我理解没错了 呵呵
魏文元(3518499) 11:20:53
不是机械化的赋值
str += "ok";
str.append("ok");
都是字符累加,以现在的电脑,一千个以内是感觉不出性能区别的。
如果是百万个以上,应该能够有区别了, stringBuilder 用时短。
顾伟伟(979605089) 11:22:21
嗯。对的。归根下来还是因为string在内存中其实是不可变的。如果实现拼接,则是另外开空间,也就是三个复本的存在
魏文元(3518499) 11:23:22
我的感觉都是不可变的。
顾伟伟(979605089) 11:23:44
我还是感觉stringbuilder是可变的。
魏文元(3518499) 11:24:04
我是以 C++ 来理解的。
顾伟伟(979605089) 11:24:38
用C++来理解,那么你就理解到最后的char上面了。那肯定都是不可变的了
魏文元(3518499) 11:24:42
存放东东要先开辟空间,而且 StringBuilder 的时候,有分配空间的限制,所以当用完的时候,再开辟内存空间才能继续。
顾伟伟(979605089) 11:25:41
stringbuilder需要分配空间,我感觉是用出示空间来放一个char数组。当然本来都是空的。这样你操作的时候就是对号入座,对这char数组的具体位置赋值 。其实就是stringbuilder的char数组中的元素是可以操作的。但是string中的char数组不可以操作。如果操作string的char数组,其实就是重新分配一个char数组,而不是对chat数组上面的char直接操作
魏文元(3518499) 11:27:02
对了,区别就在于 String 的调用 concat 方法会新建立一个 String 对象,而 StringBuilder 的 append 方法返回的还是原来对象的应用。
String 对象是不可改变的。每次使用 System.String 类中的方法之一或进行运算时(如赋值、拼接等)时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。而 StringBuilder 则不会。在需要对字符串执行重复修改的情况下,与创建新的 String 对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,则可以使用 System.Text.StringBuilder 类。
魏文元(3518499) 11:27:46
虽然 StringBuilder 对象是动态对象,允许扩充它所封装的字符串中字符的数量,但是您可以为它可容纳的最大字符数指定一个值。此值称为该对象的容量,不应将它与当前 StringBuilder 对象容纳的字符串长度混淆在一起。 例如,可以创建 StringBuilder 类的带有字符串"Hello"(长度为 5)的一个新实例,同时可以指定该对象的最大容量为 25。当修改 StringBuilder 时,在达到容量之前,它不会为其自己重新分配空间。当达到容量时,将自动分配新的空间且容量翻倍。可以使用重载的构造函数之一来指定 StringBuilder 类的容量。
顾伟伟(979605089) 11:28:16
嗯,这个我知道
陈浩(461825637) 11:28:16
StringBuilder在生命变量的过程中是可以我们自己来分配他的大小的,如果实际的内容超出内存空间,他会自动翻倍
魏文元(3518499) 11:29:25
不知道他翻倍的时候,是复制字符串还是用标记的方式开辟一个新空间,然后用逻辑连接。
不过不论哪一种,性能都会提升的。
顾伟伟(979605089) 11:29:52
这个其实就是堆栈内存管理等思想。我理解的。其实这个里面还有一个算法,用于控制数组实际大小在原始分配大小边界上的频繁变化
接口与抽象类:
接口和抽象类是c#里面两种完全不同的编程方式。抽象类是针对实现的继承,而接口是针对接口的继承。
抽象类的使用动机是为了抽象共享,从而达到派生类对于抽象类的修改或者扩展。重点是部分成员的共享,减少编码量,提高代码复用率。所以在制定抽象类模型的时候,我们更关注的是我们手头的具体类有多少部分是可以共享的。因为此动机,抽象类可以包含正常类包含的一切,为的是可以代码复用最大化。
接口则是针对接口编程的,真正面向对象的编程思想,使用接口其实是在运用依赖倒转原则,我们的注意力在事物的外在表现上面,而不再纠缠于内部实现。所以在抽象接口模型的时候,我们更多的注意力在对外约定。我们不考虑内部实现的细节问题,只关注外部约定。从而保证客户程序只需要了解所实现的接口,就知道该模块提供的服务。其实面向接口也可以解释为面向服务编程。也正因为接口的这些动机适用场景,使得在语法上接口内部仅能包含声明,不得有实现体,更不可以有成员变量,但可以有属性。也因为此接口的成员不可以被访问修饰符修饰。总之面向实现的所有思想在接口中都不可以适用。
其实现在是说明白了接口和抽象类的动机问题,接口是针对外部约定的,而抽象类则是针对内部实现。所以有人会这样调侃:
接口是用来给外部人员调用的.做项目的时候,只需要提供接口给外部调用就好了.别人不需要管你是如何实现的,就象主板上的插槽一样.外部只需要这是网卡插槽就行了.不知道知道具体实现.抽象类是用来给内部人员调用的.如果一个类有几个派生类,那么可以把这个类定义成抽象类.Team里的成员可以根据实际情况来使用不同的实现方式来实现这个抽象类.
当然上面这种调侃也反映了很深的理解。在论坛上面看到一个比较抽象的理解,感觉很好,拿过来用下:当描述一组方法的时候使用接口。当描述一个虚拟的物体的时候使用抽象类。所以也有人用is-a(抽象类)与like-a(接口)来描述两者之间的关系。
所以很多时候其实这两个使用的取舍问题主要看我们动机是什么,例如乌鸦、麻雀,当我们想描述鸟这个虚拟的物体的时候,我们会用抽象类,但是假如我们只是关注于飞这个方法,那么我们则会选用接口。很多时候我们会发现接口和抽象类都可以实现,但是其实不同的取舍已经反映出我们对于问题的理解深度。
还有,因为接口与抽象类的不同动机,使得表现出来也有很多不同,接口是一组方法,则一个类可以从多个地方接受方法规范,就像一个人可以受到多种机构管理。抽象类却是一个虚拟物体,所以一个类只能继承自一个抽象类,就像一个人只有一个爸爸。