Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么StringBuilder比字符串操作快,但是列表<;T>;比LinkedList快<;T>;?_C#_Arrays_String_List_Linked List - Fatal编程技术网

C# 为什么StringBuilder比字符串操作快,但是列表<;T>;比LinkedList快<;T>;?

C# 为什么StringBuilder比字符串操作快,但是列表<;T>;比LinkedList快<;T>;?,c#,arrays,string,list,linked-list,C#,Arrays,String,List,Linked List,所以我们被告知,当您对一个字符串执行多个操作时,应该使用StringBuilder。因此,我们应将其替换为: string s = ""; foreach (var item in items) // where items is IEnumerable<string> s += item; 我假设StringBuilder内部保存对每个附加字符串的引用,然后根据请求进行组合。让我们将其与HybridDictionary进行比较,HybridDictionary对前10个元

所以我们被告知,当您对一个字符串执行多个操作时,应该使用StringBuilder。因此,我们应将其替换为:

string s = "";
foreach (var item in items) // where items is IEnumerable<string>
    s += item;
我假设StringBuilder内部保存对每个附加字符串的引用,然后根据请求进行组合。让我们将其与HybridDictionary进行比较,HybridDictionary对前10个元素使用LinkedList,然后在列表增长超过10时交换到哈希表。正如我们在这里看到的一样,少量的引用=linkedList,否则会产生越来越多的数组块

让我们看看列表是如何工作的。从列表大小开始(内部默认值为4)。向内部数组中添加元素,如果数组已满,则创建一个大小为当前数组两倍的新数组,复制当前数组的元素,然后添加新元素并使新数组成为当前数组

你能看出我对性能优势的困惑吗?对于字符串以外的所有元素,我们创建新数组,复制旧值并添加新值。但对于弦来说,这不好吗?因为我们知道“a”+“b”从两个旧引用“a”和“b”中生成一个新的字符串引用

希望我的问题不要太混乱。为什么字符串连接和数组连接之间似乎有双重标准(我知道字符串是字符数组)

字符串:创建新引用是不好的

T:T在哪里!=String:创建新的参考资料很好

编辑:也许我在这里真正想问的是,什么时候创建新的、更大的数组并跨多个数组复制旧值开始比在堆中随机放置对象的引用更快

双重编辑:我所说的更快是指读取、写入和查找变量,而不是插入或删除变量(例如,LinkedList在插入变量方面会非常出色,但我不在乎)

最终编辑:我不关心StringBuilder,我感兴趣的是将数据从堆的一部分复制到另一部分以进行缓存对齐所花费的时间,而不是仅仅从cpu获取缓存未命中,并在整个堆中都有引用。什么时候一个比另一个快*

因此,我们应将其替换为:

string s = "";
foreach (var item in items) // where items is IEnumerable<string>
    s += item;
不,你不应该。第一个案例显示了可以在编译时发生的字符串连接,并用运行时发生的字符串连接代替了它。前者更可取,执行速度也比后者快

当编译时不知道要合并的字符串数时,使用字符串生成器非常重要。通常(但并非总是)这意味着在循环中压缩字符串


早期版本的String Builder(4.0之前,如果内存可用的话)在内部看起来确实或多或少像一个
列表
,而在4.0之后,它看起来更像一个
链接列表
,这是正确的。但是,在循环中使用
StringBuilder
和使用常规字符串连接之间的关键区别不是链表样式之间的区别,在链表样式中,对象包含对“链”中下一个对象的引用以及一种基于数组的样式,其中内部缓冲区过度分配空间,并根据需要偶尔重新分配,而是可变对象和不可变对象之间的区别。传统字符串连接的问题在于,由于字符串是不可变的,因此每次连接都必须将两个字符串中的所有内存复制到一个新字符串中。使用
StringBuilder
时,只需将新字符串复制到某种类型的数据结构的末尾,保留所有现有内存。什么类型的数据结构在这里不是非常重要;我们可以依靠Microsoft使用一种结构/算法,这种结构/算法已被证明在最常见的情况下具有最佳的性能特征。

在我看来,您将列表的大小调整与字符串表达式的计算混为一谈,并假设两者的行为应该相同

考虑您的示例:
string s=“a”+“b”+“c”+“d”

假设常量表达式(编译器将自动处理)没有优化,这将依次计算每个操作:

string s = (("a" + "b") + "c") + "d"
这将导致字符串
“ab”
“abc”
被创建为单个表达式的一部分。这是必须的,因为.NET中的
字符串是不可变的,这意味着一旦创建它们的值就不能更改。这是因为,如果字符串是可变的,那么代码如下:

string a = "hello";
string b = a;       // would assign b the same reference as a
string b += "world"; // would update the string it references
// now a == "helloworld"
如果这是一个
列表
,代码将更有意义,甚至不需要解释:

var a = new List<int> { 1, 2, 3 };
var b = a;
b.Add(4);
// now a == { 1, 2, 3, 4 }
var a=新列表{1,2,3};
var b=a;
b、 增加(4);
//现在a=={1,2,3,4}
因此,非字符串“列表”类型提前分配额外内存的原因是为了提高效率,以及在扩展列表时减少分配。
字符串
不这样做的原因是
字符串
的值在创建后永远不会更新


您对
StringBuilder
操作的假设是不相关的,但是
StringBuilder
的目的本质上是创建一个不可变的对象,以减少多个
string
操作的开销。

StringBuilder
的后备存储是
char[]
可根据需要调整大小。在调用字符串上的
StringBuilder.ToString()
之前,不会将任何内容转换为字符串

List
的备份存储是一个
T[]
,可根据需要调整大小

像这样的问题

string s = a + b + c + d ;
string t1 = c +  d ;
string t2 = b + t1 ;
string s  = a + t2 ;
编译器将其解析为

  +
 / \
a   +
   / \
  b   +
     / \
    c   d
而且,除非它能看到优化的机会,否则就做类似的事情

string s = a + b + c + d ;
string t1 = c +  d ;
string t2 = b + t1 ;
string s  = a + t2 ;
从而创建两个临时变量和最后一个字符串。机智
int x = 12 / 3 ;
int x = 4 ;