使用 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<int, int>
是引用类型,您重复该值,该值存储在内存中的同一位置。 sources 表明,RepeatIterator
在循环中返回相同的值,无需复制
@Aven 你正在修改同一个实例
var graph = Enumerable.Range(0,n).Select(i => new Dictionary<int, int>()).ToList();
我更喜欢循环。
@Aven 此方法不会创建值的新副本,它只是重复该值。在引用类型的情况下,每个值都指向内存中的相同位置。因此你会得到一个不正确的输出,因为修改同一个实例并且不能使用第二种方式将[0,2,500]
添加到字典中(在这种情况下,值2
的键已经存在)
【参考方案1】:
Enumerable.Repeat
只是重复传递的值n
次。您创建一个字典,将其传递给Repeat
,它会生成一个n
对该字典的引用列表。
如果您想基于函数生成序列,则可以使用 MoreLinq Generate
扩展名。
【讨论】:
或类似Enumerable.Range(0, n).Select(_ => new Dictionary<...>())
以上是关于使用 Enumerable.Repeat 进行容量列表初始化的问题 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法在 C# 中使用 Enumerable.Repeat 方法重复 Func<T, T> ?
Kotlin 协程Channel 通道 ② ( Channel 通道容量 | Channel 通道迭代 | 使用 iterator 迭代器进行迭代 | 使用 for in 循环进行迭代 )
Kotlin 协程Channel 通道 ② ( Channel 通道容量 | Channel 通道迭代 | 使用 iterator 迭代器进行迭代 | 使用 for in 循环进行迭代 )