为什么删除项目后堆栈,队列和列表不缩小内部数组?

Ser*_*diy 5 .net c# arrays collections data-structures

在.NET中StackQueue和和List(以及它们的通用实现)在内部使用数组保存项目。

添加新元素时,内部数组的大小将增加一倍Push()Enqueue()或者Add()如果已满。

问题是,为什么在删除项目时这些数据结构不内部数组减半?最好将上的阵列大小减半,或者如果阵列的大小小于四分之一,以防止浪费内存。Pop()Dequeue()Remove()

我使用以下代码检查此行为:

var s = new Stack<int>();
for (int i = 0; i <= 512; i++) s.Push(i); // Increases the array capacity to 1024.
for (int i = 0; i <= 512; i++) s.Pop();   // Doesn't decrease array capacity.

// Stack is empty, but internal array capacity is still 1024:
var fieldInfo = typeof(Stack<int>).GetField("_array", BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine((fieldInfo.GetValue(s) as int[]).Length);
Run Code Online (Sandbox Code Playgroud)

Chr*_*s J 6

最有可能的效率。

缩小和增长阵列涉及到在内存中复制阵列。您不能简单地将少量的新内存标记到现有分配上,然后再将其从分配中删除:分配在操作系统中的大小是固定的,因此,如果您要增加数组的大小,实际上要分配新的内存块,请将所有内容从源复制到目标,然后删除旧的分配。这也是收缩的类似操作。这可能很耗时。

现在,鉴于数组已经增长到给定的大小,对于.NET(或任何其他框架)而言,它是该数组的最佳大小的一个很好的指标。即使从其中删除了项目,数组也有可能再次增长到相同的大小:.NET不是Mystic Meg,因此不会知道您永远不会再使用该数组的块。因此,与不断分配和取消分配内存块相比,增大分配直到不再需要再次增长才更有效,然后只担心在变量超出范围并成为该变量的目标时清理它GC。

  • 话虽这么说,将“ GreedyList”,“ GreedyStack”和“ GreedyQueue”类引入框架中,以解决较小的内存占用比执行时间更重要的情况,这是否有意义? (2认同)
  • 如果你想要一个小的内存占用,你不会使用像 .NET 或 Java 这样你对内存几乎没有控制的东西:-) 这听起来可能很傲慢,但这是使用正确工具来完成工作的一个例子。也就是说,如果您真的需要它,那么在删除项目时为“释放”内存(对于给定的“释放”值,因为您依赖于垃圾收集器)编写代码应该不会那么难。 (2认同)