【发布时间】:2012-07-18 09:56:33
【问题描述】:
我偶然发现了以下问题。
我想要一个包含从 1 到 100.000.000 的所有数字的哈希集。
我尝试了以下代码:
var mySet = new HashSet<int>();
for (var k = 1; k <= 100000000; k++)
mySet.Add(k);
该代码没有成功,因为我在 4900 万左右发生了内存溢出。这也很慢,内存增长过快。
然后我尝试了这个。
var mySet = Enumerable.Range(1, 100000000).ToHashSet();
其中 ToHashSet() 为以下代码:
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
{
return new HashSet<T>(source);
}
我再次遇到内存溢出,但我能够输入更多的数字然后使用之前的代码。
起作用的事情如下:
var tempList = new List<int>();
for (var k = 1; k <= 100000000; k++)
tempList.Add(k);
var numbers = tempList.ToHashSet();
在我的系统上大约需要 800 毫秒来填充 Enumerable.Range() 只需要 4 个滴答声的 tempList!
我确实需要那个 HashSet,否则查找值需要很长时间(我需要它是 O(1)),如果我能以最快的方式做到这一点,那就太好了。
现在我的问题是:
为什么前两种方法会导致内存溢出,而第三种方法不会?
HashSet 在初始化时对内存有什么特殊作用吗?
我的系统有 16GB 内存,所以当我遇到溢出异常时我很惊讶。
【问题讨论】:
-
需要注意的一点是
Enumerable.Range非常快,因为它在运行时实际上并没有生成任何东西。只有在使用它时(即在ToHashSet调用中)它才真正开始生成数字。 -
@Chris 不知道。谢谢:)。
-
它与所有 linq 类型可枚举的东西都是一样的。如果您对枚举或 Select 或其他任何基本上返回更多 ienumerables 的东西执行了 Where ,它将推迟它们的执行,直到它们被使用。知道这一点很有用,因为您可能会因为这种行为而遇到一些问题(尽管我想不出一个简洁的例子)。
标签: c# performance memory collections hashset