使用 Enumerable.Repeat 进行容量列表初始化的问题 [重复]

Posted

技术标签:

【中文标题】使用 Enumerable.Repeat 进行容量列表初始化的问题 [重复]【英文标题】:The problem with List Initialization with capacity using Enumerable.Repeat [duplicate] 【发布时间】:2020-05-12 07:06:29 【问题描述】:

如果我们试图用容量和默认值初始化一个列表,就像我们在 C++ 中用大小和默认值初始化一个向量一样,比如我正在创建一个字典列表来构建一个带有权重的图,有通常有两种方法:

1) 使用for循环手动构建列表:

var graph= new List<Dictionary<int, int>>();
for (var i = 0; i < n; i++)

    graph.Add(new Dictionary<int, int>());

2) 使用 Enumberable.Repeat() 初始化列表:

var graph= Enumerable.Repeat(new Dictionary<int, int>(), n).ToList();

假设现在我们有一个带有权重的节点对(边)列表,并且想要在此之上构建一个图,我们会这样写:

foreach (var list in lists) // List = [[0, 1, 100],[1,2,100],[0,2,500]]

    int a = list[0], b = list[1], weight= list[2];
    if (!graph[a].ContainsKey(b))
        graph[a].Add(b, weight);

虽然这两种方法理论上都应该有效,但结果却大不相同。 当我尝试使用以下代码打印图表时:

for (var i = 0; i < graph.Count; i++)

    var node = graph[i];
    foreach (var pair in node)
    
        Console.WriteLine($"i -> pair.Key with price pair.Value");
    

1) 的结果如下所示:

// Correct output
0 -> 1 with price 100
0 -> 2 with price 500
1 -> 2 with price 100

但是 2) 的结果看起来像:

// Incorrect result
0 -> 1 with price 100
0 -> 2 with price 100
1 -> 1 with price 100
1 -> 2 with price 100
2 -> 1 with price 100
2 -> 2 with price 100

在这种情况下,Repeat 方法的行为似乎很奇怪,在 Doc 上,没有提到这种行为。

我对这种行为的看法是重复方法在内存中创建 ONLY ONE 值并将所有列表条目指向相同的内存位置。在这种情况下,内存中只创建一个字典,而不是为每个列表条目创建一个新字典。字典上的所有操作实际上都发生在同一个位置。但这并不能解释第二个结果中的奇怪输出。

有什么想法吗?这是 Enumerable.Repeat() 方法中的错误还是我做错了?用默认值初始化列表的更好方法是什么?

【问题讨论】:

我认为不会。 Dictionary&lt;int, int&gt; 是引用类型,您重复该值,该值存储在内存中的同一位置。 sources 表明,RepeatIterator 在循环中返回相同的值,无需复制 @Aven 你正在修改同一个实例 var graph = Enumerable.Range(0,n).Select(i =&gt; new Dictionary&lt;int, int&gt;()).ToList(); 我更喜欢循环。 @Aven 此方法不会创建值的新副本,它只是重复该值。在引用类型的情况下,每个值都指向内存中的相同位置。因此你会得到一个不正确的输出,因为修改同一个实例并且不能使用第二种方式将[0,2,500]添加到字典中(在这种情况下,值2的键已经存在) 【参考方案1】:

Enumerable.Repeat 只是重复传递的值n 次。您创建一个字典,将其传递给Repeat,它会生成一个n 对该字典的引用列表。

如果您想基于函数生成序列,则可以使用 MoreLinq Generate 扩展名。

【讨论】:

或类似Enumerable.Range(0, n).Select(_ =&gt; new Dictionary&lt;...&gt;())

以上是关于使用 Enumerable.Repeat 进行容量列表初始化的问题 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在 C# 中使用 Enumerable.Repeat 方法重复 Func<T, T> ?

为啥与空列表连接时返回列表但与新列表连接时不返回?

创建一个充满-1的数组; [复制]

Kotlin 协程Channel 通道 ② ( Channel 通道容量 | Channel 通道迭代 | 使用 iterator 迭代器进行迭代 | 使用 for in 循环进行迭代 )

Kotlin 协程Channel 通道 ② ( Channel 通道容量 | Channel 通道迭代 | 使用 iterator 迭代器进行迭代 | 使用 for in 循环进行迭代 )

Redis07-Redis单节点容量问题,twemproxy,predixy的使用