2014-09-01 32 views
4

我总是想知道什么时候可变性。为什么.net设计人员只开发了stringbuilder类来实现字符串类&的可变性而不是intbuilder for int考虑到int &字符串的实现方式完全相同,不管其数据类型如何。为什么只有字符串是不可变的,而不是其他数据类型

+0

这使得良好的阅读:http://channel9.msdn.com/forums/TechOff/58729-Why-are-string-types-immutable-in-C/ – jbutler483 2014-09-01 12:08:59

+0

你为什么想让一个int可变吗?值类型根据定义是不可变的(1始终为1)。 – 2014-09-01 12:14:26

+0

@TimSchmelter我不想通过让int mutable来达到任何目的 – Tuscan 2014-09-01 12:19:30

回答

3

许多其他语言提供字符串类似的设计:与Java StringBufferStringBuilder,斯卡拉与StringBuilder,Python来MutableString though there are other, beter solutions in Python。在C++中,字符串是可变的,所以不需要构建器。

之所以字符串建设者存在是:

  1. 许多语言定义字符串为不可变的(任何更改都需要在内存中一个新的对象)
  2. 字符串往往要大,比整数更大
  3. [1]和[2]组合原因低效

为什么助洗剂不对于int存在的原因:

  1. 它是由本身简单的数据结构
  2. 大多数CPU已经优化的指令来处理简单的数字(添加,带走等)
  3. 大多数CPU将有效地处理[2]仅在一个指令或几个周期,通过寄存器或快速的CPU缓存
  4. [2]和[3]结合消除对优化需要
  5. 这里有一点需要变异int类型本身,但是,如果你需要,你可以使用BitConverterbinary shift operations
-2

StringBuilder被添加为performance reasons,特定于字符串。 intbuider没有意义,因为int是一个简单的类型:可变的int将没有意义。正如它在another answer中解释:

+2

_“intbuider没有意义,因为int已经是不可变的了”_字符串也是不可变的。OP是问为什么没有intbuilder使它也是可变的,所以你的答案错过了主题.. – 2014-09-01 12:11:03

+0

@Tim,'String'是不可变的,但是'StringBuilder'不是,这就是它被添加的原因,不是吗?那么,什么是错的? – 2014-09-01 12:14:35

+1

是的,OP询问为什么不存在'IntBuilder',因为'int'是不可变的。 'String'也是不可变的,并且有一个'StringBuilder'。你的解释指出没有,因为'int'已经是不可变的了。我们正在圈圈。 – 2014-09-01 12:16:19

-1

我认为你没有考虑的是如何在现代 计算机工作的内存。

当你问操作系统给你更多的内存来存储数据(如 字符串),其空间可以在内存地址空间的任何地方......

所以,让我们假设你问的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被设计为不可变的。这也是为什么不可变类通常是首选的原因。

我也只想澄清一下,这个答案可以参考多种编程语言。

+2

这回答这个问题不是吗? – 2014-09-01 12:24:24

2

为了帮助您理解为什么没有IntBuilderLongBuilder,DecimalBuilder等)考虑如果要设计它们,您将对这些类进行什么操作。关于一个数字几乎不需要“构建”:与可以追加,插入和删除的字符串不同,数字只能用新数字替换。

注意,即使在情况下,当新的值是基于旧值(比方说,在Add或假想的IntBuilder一个Multiply方法),它本质上是一个更换操作,因为IntBuilder能够保存单个值。因此,您的IntBuilder最终会看起来像这样:

class IntBuilder { 
    public int Value { get; set; } 
} 

这就是你需要有一个可变Int32。也许一组好的ToString,EqualsGetHashCode覆盖也是有用的;也许,一些转换运营商让你把IntBuilder作为int在某些情况下也会派上用场。但是在上面的实现中捕获了这个类的本质:所有其他的“构建”操作都可以在IntBuilder上以Value的分配来建模。但是,这很难说是有用的,因为两个原因:

  • 您可以使用原始的int用于突变的单一方法的范围内的整数,
  • 您可以通过引用传递原始int如果你一定要实现其他方法的突变。
0

这个问题肯定是重复的,但我不能找到什么。

你是问错了问题,我们都期望最大的价值类型为不可变的,并期望所有基本类型成为价值类型。然而,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团队试图解决的问题。

(大多数编程语言很难适应串入式系统。)

-1

每次生成的字符串创建堆一个新的实例,与诠释平时操作都在栈上完成

// 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字符串被复制到堆上的不可变字符串。

演示:https://dotnetfiddle.net/plteCr

相关问题