Dictionary.Clear与new Dictionary()之间的区别

31

Dictionary.Clearnew Dictionary() 在 C# 中有什么关键区别?哪种方法适用于哪些情况?


9个回答

38
Dictionary.Clear()会删除字典中的所有KeyValue对。使用new Dictionary()将创建一个新的字典实例。
仅当旧版本的字典没有被其他引用引用时,创建新字典将使整个字典及其内容(未在其他地方引用)可供垃圾回收器清理。 Dictionary.Clear()会使KeyValue对可供清理。
实际上,这两个选项通常会产生非常相似的效果。区别在于在方法内部使用时会发生什么:
void NewDictionary(Dictionary<string,int> dict)
{
   dict = new Dictionary<string,int>(); // Just changes the local reference
}

void  ClearDictionary(Dictionary<string,int> dict)
{
   dict.Clear();
}

// When you use this...
Dictionary<string,int> myDictionary = ...; // Set up and fill dictionary

NewDictionary(myDictionary);
// myDictionary is unchanged here, since we made a new copy, but didn't change the original instance

ClearDictionary(myDictionary);
// myDictionary is now empty

2
@divo - 我认为他的意思是复制引用,而不是对象本身的副本。 - John Rasch
@John:那正是我想表达的。它是一个副本——字典引用的副本。 - Reed Copsey
如果将字典传递到一个方法中,并将其设置为新实例并添加元素,则需要在参数参数中添加out修饰符,否则返回的变量将没有这些元素。 out参数会创建进一步的问题,因为它要求在方法内的所有控制分支(if / switch)中实例化字典,这将删除现有元素。 因此,如果您需要将字典传递给有时会删除所有元素并返回添加元素的方法,则最好使用Dictionary.Clear()。 - Will
"Dictionary.Clear()将使KeyValue对可供清理。您所指的“KeyValue对”是哪些?.NET Framework字典实现不使用System.Collections.Generic.KeyValuePair来内部存储数据:来源。" - Hugh W

24

正如广泛报道的那样,它们的效果基本相同。 Clear()new 都会给你一个全新的Dictionary,实质上放弃其中对象的引用,使它们可以被回收如果它们变成未根对象。

技术上,如果您要重新填充与之前相同大小的字典,则 .Clear()new 有优势。这是因为它已经在内部调整大小以容纳您之前拥有的对象数量。创建一个新 Dictionary 将具有具有默认大小的新内部哈希表,并且必须在向其中添加项时重新扩展。

我还建议它们传达完全不同的意图,当您处理与之前相同的上下文时,应在代码中使用 .Clear()。创建新的Dictionary意味着您即将开始处理与旧Dictionary不同的一些新逻辑,而使用Clear()则表示您确实想要重置迄今为止与其相关的所有内容。


6
创建一个新的字典会有一个新的内部哈希表,默认大小不一定,你可以使用构造函数重载来设置初始容量。 - Joe
6
是的,我假设只是使用.Clear()而不是新建一个Dictionary()。 - womp

13

Dictionary.Clear()
这将删除Dictionary中的所有键/值对。垃圾回收器将在下一次垃圾回收周期清除这些项所占用的内存。MSDN

new Dictionary()
创建一个新的Dictionary对象并放弃原始对象。内存将在下一次垃圾回收周期期间被清除。


那篇 MSDN 文章没有提到任何关于释放资源的内容。 - Jon B
4
需要考虑的一点是,如果你的字典非常大,并且你确定在清空后会加载大约相同数量的数据,则字典的容量不会被重置,这将使使用Dictionary.Clear()方法更加有吸引力。 - overslacked
@overslacked - 真该死,我刚刚花了时间写了一个建议完全就是那个的答案。 - womp
你忘记了当你分配一个新的字典时,其他引用并没有被“清除”...我几乎要因为这个错过点赞。 - Dykam
1
@Dykam,假设条目没有根源(如果您在清除/新建之间进行决策,则我认为这是安全的),提到原始对象被放弃似乎已经足够警告了。 @womp,点赞!我还没有对其效果进行过分析,也不知道它现在是否像以前那样令人担忧,所以我只是跟随领导者。 - overslacked
垃圾回收器将在下一个垃圾回收周期清除这些项目的内存,但这并不完全准确或有意义。例如,字典内容的其他引用可能会使它们保持活动状态。 - Hugh W

5

我认为关键的区别在于,如果您调用Dictionary.Clear(),则会清除所有对该字典的引用。如果您使用new Dictionary(),那么您在处理当前引用时将被清除(在某种程度上),但您拥有引用的所有其他位置都不会被清除,因为它们仍然将指向旧字典。

希望这段代码能简单地说明问题:

public void Method()
{
    Dictionary<int, int> d1 = new Dictionary();

    d1.Add(1,1);
    d1.Add(2,3);

    Dictionary<int, int> d2 = d1;

    //this line only 'clears' d1
    d1 = new Dictionary();

    d1.Add(1,1);
    d1.Add(3,5);
    d2.Add(2,2);

    //writes '2' followed by '1'
    Console.WriteLine(d1.Count);
    Console.WriteLine(d2.Count);
}

如果我使用了d1.Clear(),那么d1和d2将保持同步,随后的添加操作将会同时作用于二者。两个WriteLine调用都会输出'3'。


4

Dictionary.Clear会移除(但不销毁)实例中的所有项目,而new Dictionary()将创建一个全新的实例,该实例恰好为空。两者之间可能存在一些微妙的影响。请考虑以下示例。

void Example1()
{
  var collection = new Dictionary<string, string>();
  collection.Add("key1", "value1");
  collection.Add("key2", "value2");
  foreach (var kvp in collection)
  {
    collection = new Dictionary<string, string>();
    Console.WriteLine(kvp);
  }
}

void Example2()
{
  var collection = new Dictionary<string, string>();
  collection.Add("key1", "value1");
  collection.Add("key2", "value2");
  foreach (var kvp in collection)
  {
    collection.Clear();
    Console.WriteLine(kvp);
  }
}

在第一个示例中,将打印原始集合中的所有内容。这是因为在将引用变量重新分配给新实例之前,枚举器是从引用变量创建的。
在第二个示例中,在枚举期间清除了集合,这将导致“InvalidOperationException”异常,因为集合已被修改。

2

我认为提供.Clear()函数是为了方便在Dictionary以只读属性暴露时,可以删除所有项。但是,如果没有以这种方式暴露,并且你完全掌控,则实例化一个新的Dictionary可能更容易。两者之间可能会有轻微的性能差异。


1

Dictionary.Clear() (源代码) 会重置字典的内部数据结构。在这些内部使用的Entry 结构将会被垃圾收集器清理,同时只能通过这些结构访问的键和值也将被清理。字典的容量将保持不变,尽管自创建以来可能已经增长。

new Dictionary() 分配了一个新对象。新字典的初始容量将是默认值或您指定的值,具体取决于您使用的构造函数。这个容量可能与现有字典的容量不同,如果您使用了 Clear() 方法,则情况可能会有所不同。是否重要取决于您的具体情况。

Dictionary.Clear() 提供了一种清空字典的方法,在其他引用仍然存在但无法更新为指向新实例的情况下使用。


0
如果您有多个引用指向该字典并且您清除它,那么所有引用都会受到影响,而如果您创建一个新的字典,您只会影响一个新的引用。我想这取决于您所追求的影响范围。显然,调用“new”将为您提供一个“已清除”的字典,但您可能会失去其他有用的状态信息。

-1

Dictionary.clear会清除字典中的所有项。new Dictionary会在内存中创建一个新的字典对象,同时将您之前的字典孤立起来,以便垃圾收集器稍后清理。


我们能比“很可能”做得更好吗?这将是一个关键问题。 - Jon B
1
我认为字典永远不会调用其包含项的Dispose方法;如果这些项是可处理的,那么您应该编写显式代码来处理它们。 - ChrisW
1
为什么会对任何东西调用dispose?仅仅因为它们在字典中并不意味着它们没有在其他地方使用。那将是非常有缺陷的。它所做的只是将哈希表桶置为空值。 - womp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接