【发布时间】: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的键已经存在)
标签: c# enumerable