我总是想知道什么时候可变性。为什么.net设计人员只开发了stringbuilder类来实现字符串类&的可变性而不是intbuilder for int考虑到int &字符串的实现方式完全相同,不管其数据类型如何。为什么只有字符串是不可变的,而不是其他数据类型
回答
许多其他语言提供字符串类似的设计:与Java StringBuffer和StringBuilder,斯卡拉与StringBuilder,Python来MutableString though there are other, beter solutions in Python。在C++中,字符串是可变的,所以不需要构建器。
之所以字符串建设者存在是:
- 许多语言定义字符串为不可变的(任何更改都需要在内存中一个新的对象)
- 字符串往往要大,比整数更大
- [1]和[2]组合原因低效
为什么助洗剂不对于int存在的原因:
- 它是由本身简单的数据结构
- 大多数CPU已经优化的指令来处理简单的数字(添加,带走等)
- 大多数CPU将有效地处理[2]仅在一个指令或几个周期,通过寄存器或快速的CPU缓存
- [2]和[3]结合消除对优化需要
- 这里有一点需要变异int类型本身,但是,如果你需要,你可以使用BitConverter或binary shift operations
StringBuilder
被添加为performance reasons,特定于字符串。 intbuider
没有意义,因为int是一个简单的类型:可变的int将没有意义。正如它在another answer中解释:
_“intbuider没有意义,因为int已经是不可变的了”_字符串也是不可变的。OP是问为什么没有intbuilder使它也是可变的,所以你的答案错过了主题.. – 2014-09-01 12:11:03
@Tim,'String'是不可变的,但是'StringBuilder'不是,这就是它被添加的原因,不是吗?那么,什么是错的? – 2014-09-01 12:14:35
是的,OP询问为什么不存在'IntBuilder',因为'int'是不可变的。 'String'也是不可变的,并且有一个'StringBuilder'。你的解释指出没有,因为'int'已经是不可变的了。我们正在圈圈。 – 2014-09-01 12:16:19
我认为你没有考虑的是如何在现代 计算机工作的内存。
当你问操作系统给你更多的内存来存储数据(如 字符串),其空间可以在内存地址空间的任何地方......
所以,让我们假设你问的OS存储一个10个字符的字符串(10 * 2个字节= 20个字节),所以你有你的20个字节,并把你的 字符串数据放在那里。
现在的程序员认为,让我们把另一个字符在 是10个字符的字符串的结束......所以你问另外2个字节的OS(一 单个字符),但发现这两个额外的字节位于 内存中的一些通用和随机位置....问题;你不能只是 在一个地方有10个字符,另外1个在另一个地方...所以到 解决这个问题每次你增加一个字符串的大小字符串是 销毁然后总长度是从内存池请求。
基本也是这样,它只是透明的。你的字符串是 被破坏,你只是没有基本的内存指针,所以不要 的通知。
这里是一个比喻来帮助(我能想到的最好的)。
你和一些朋友去TicketMaster网站尝试购买最新演唱会的门票5 ;你的座位编号为523,524, 525,526和527.然后你的另一位朋友建议他们想要 也来...所以你再次去TicketMaster网站 希望你可以购买座位528或522,所以你可以坐在旁边一个 另一个......但该网站不会让你选择你想坐的位置。
这让你两个选择。要么取消你所有的朋友的座位 并重新预订所有六个人或在最后一个人的座位上座位...但是,如果你坐在他们的其他地方,你可能无法在他们之后找到他们 ...因此,你决定选择第一个选项并取消并重新预订 的全部号码。
从我发布在OP的问题下的链接。
编辑:
http://www.programcreek.com/2013/04/why-string-is-immutable-in-java/解释究竟为什么字符串是不可变的。这包括:
- 要求在字符串池:
字符串池(字符串实习生池)是在方法区一个特殊的存储区域。当创建字符串时,如果该字符串已经存在于池中,则将返回现有字符串的引用,而不是创建新对象并返回其引用。
- 缓存Hashcode方法:
字符串的哈希码在Java中经常使用。例如,在一个HashMap中。不可变的保证hashcode始终是相同的,所以它可以兑现而不用担心变化。这意味着,每次使用时都不需要计算哈希码。这样更有效率。
- 促进其他对象的使用:
为了使这个具体的,可以考虑下面的程序:
HashSet<String> set = new HashSet<String>();
set.add(new String("a"));
set.add(new String("b"));
set.add(new String("c"));
for(String a: set)
a.value = "a";
在这个例子中,如果字符串是可变的,它的值可以被改变,这会违反set的设计(set包含不重复的元素)。这个例子是为简单起见而设计的,在真正的String类中没有值字段。
- 安全性:
字符串被广泛地用作参数对于许多java类,例如网络连接,打开文件等。如果字符串不是不可变的,则连接或文件将被更改并导致严重的安全威胁。该方法认为它连接到一台机器,但没有。可变字符串也可能导致反射中的安全问题,因为参数是字符串。
- 不可变对象是天然线程安全
由于不可改变的对象不能被改变,它们可以在多个线程之间自由共享。这消除了做同步的要求。
总之,为了提高效率和安全性,String被设计为不可变的。这也是为什么不可变类通常是首选的原因。
我也只想澄清一下,这个答案可以参考多种编程语言。
这回答这个问题不是吗? – 2014-09-01 12:24:24
为了帮助您理解为什么没有IntBuilder
(LongBuilder
,DecimalBuilder
等)考虑如果要设计它们,您将对这些类进行什么操作。关于一个数字几乎不需要“构建”:与可以追加,插入和删除的字符串不同,数字只能用新数字替换。
注意,即使在情况下,当新的值是基于旧值(比方说,在Add
或假想的IntBuilder
一个Multiply
方法),它本质上是一个更换操作,因为IntBuilder
能够保存单个值。因此,您的IntBuilder
最终会看起来像这样:
class IntBuilder {
public int Value { get; set; }
}
这就是你需要有一个可变Int32
。也许一组好的ToString
,Equals
和GetHashCode
覆盖也是有用的;也许,一些转换运营商让你把IntBuilder
作为int
在某些情况下也会派上用场。但是在上面的实现中捕获了这个类的本质:所有其他的“构建”操作都可以在IntBuilder
上以Value
的分配来建模。但是,这很难说是有用的,因为两个原因:
- 您可以使用原始的
int
用于突变的单一方法的范围内的整数, - 您可以通过引用传递原始
int
如果你一定要实现其他方法的突变。
(这个问题肯定是重复的,但我不能找到什么。)
你是问错了问题,我们都期望最大的价值类型为不可变的,并期望所有基本类型成为价值类型。然而,string是一个基本类型,它是一个引用类型。
请参阅“In C#, why is String a reference type that behaves like a value type?”,“Is string a value type or a reference type?”和“Why is string a reference type?”针对C#deign团队试图解决的问题。
(大多数编程语言很难适应串入式系统。)
每次生成的字符串创建堆一个新的实例,与诠释平时操作都在栈上完成
// increment int in loop
int c = 10;
for (int i = 0; i < 100; ++i)
{
c += 5;
}
Console.WriteLine("c: {0}", c);
c
是在栈上的变量,增量将替换值,而无需使用额外的内存。
相反:
// concat string in loop
string acc = "";
for (int i = 0; i < 100; ++i)
{
acc += "A";
}
Console.WriteLine("acc: {0}", acc);
这将创造1(string acc = "";
)+ 100(acc += "A";
)=上堆101新的对象,每个 与("A"
,"AA"
,"AAA"
等)中使用多个存储器将更多的压力在GC上,因为除了最后一个以外的所有这些实例都被收集。当字符串很长时,这会成为一个性能问题。
随着:
// concat string with StringBuilder
var sb = new StringBuilder();
for (int i = 0; i < 100; ++i)
{
sb.Append("A");
}
Console.WriteLine("sb: {0}", sb.ToString());
只有两个对象被创建(StringBuilder的和sb.ToString()
)和内部非托管存储器用于追加,而不需要建立新的中间串。只有sb.ToString()
内部unmanged字符串被复制到堆上的不可变字符串。
- 1. 为什么我可以转换为这种类型而不是其他类型?
- 2. 为什么string.search对某些字符串而不是其他字符?
- 3. 为什么有些变量是参数而其他变量不是?
- 4. 为什么使用'/'而不是其他字符来路由
- 5. 为什么不是File.new符号的参数而不是字符串?
- 6. 为什么不使用字符串缓冲区而不是不可变的字符串?
- 7. 为什么java中的字符串是不可变的?
- 8. 为什么字符串是引用类型,但行为与其他引用类型不同?
- 9. 为什么在我的字符串中有`=`而不是`\ x`?
- 10. 为什么MongoDB.Bson.BsonExtensionMethods.ToBson返回字节数组而不是字符串?
- 11. 为什么Python字符串和元组是不可变的?
- 12. 为什么不是“如果”而不是“其他”?
- 13. SSIS:数据类型只是不能从字符串转换
- 14. 为什么我可以在这里用int而不是字符串查询? PHP的MySQL数据类型
- 15. Crystal Reports获取字符串数据类型而不是日期
- 16. 是否有可能NSLog只有我通过的字符串,而不是其他信息?
- 17. 不可变的值类型[字符串]只有变异的成员名为append
- 18. 为什么不是类型(字符串)等于“<class 'str'>”?
- 19. C++什么是字符串文字的数据类型?
- 20. 提取字符串,它是存在于一个字符串,而不是其他
- 21. 什么是#define字符串的类型?
- 22. 为什么内部静态字符串是不可访问,类
- 23. 为什么Python返回整数而不是字符串
- 24. 为什么这会循环'数据'而不是'新类型'?
- 25. NSOrderedDescending而不是NSOrderedSame为相等的字符串,为什么?
- 26. 字符串数组加字符串不是错误,为什么?
- 27. 为什么查询有时挂而不是其他
- 28. 字符串比较==只工作,因为字符串是不可变的?
- 29. 为什么I18n.locale不是字符串
- 30. 为什么有些类变量看起来像静态而其他类不变?
这使得良好的阅读:http://channel9.msdn.com/forums/TechOff/58729-Why-are-string-types-immutable-in-C/ – jbutler483 2014-09-01 12:08:59
你为什么想让一个int可变吗?值类型根据定义是不可变的(1始终为1)。 – 2014-09-01 12:14:26
@TimSchmelter我不想通过让int mutable来达到任何目的 – Tuscan 2014-09-01 12:19:30