C#如何动态地为List <t>分配内存?

时间:2018-07-07 22:58:07

标签: c# .net list memory dynamic-memory-allocation

LukeH'swhat is the max limit of data into list<string> in c#?

  

从理论上讲,List的当前实现中可以存储的最大元素数量为Int32.MaxValue-刚好超过20亿。

我们看到一个列表可以携带大量物品。我假设编译器不会为T的每个新实现释放20倍于List<T>大小的空间,那么列表如何动态增长?它是否具有指向内存中非连续空间的指针?

3 个答案:

答案 0 :(得分:14)

List<T>类被实现为在内部使用内部$posts = $posts->filter(function ($post, $key) { $isHasMoreFiveComments = false; foreach ($post['comments'] as $comment) { if ($comment['reputation'] > 5) { $isHasMoreFiveComments = true; break; } } return $isHasMoreFiveComments; }); 数组。如果使用List<T>(int)构造函数对其进行初始化,它将分配指定大小的数组。如果使用默认构造函数,它将使用默认容量4,但是在这种情况下,只会在第一次添加时分配该数组。

每次将元素添加到列表时,它都会首先检查是否已达到容量(即现有T[]是否等于Count)。如果是这样,它将创建一个新数组,其大小是前一个数组的两倍,将所有现有元素复制到其中,然后继续编写新元素。这将在以后的元素添加中无限期地发生,直到达到您引用的硬限制(Capacity)。

在性能方面,这意味着元素的添加是O(1)或O(n)操作,具体取决于是否需要增加容量(如在Add下讨论)。但是,由于每当需要增加容量时 double ,随着列表的增加,这种重新分配的频率就会呈指数下降。例如,从4开始,容量增加将发生在4、8、16、32、64、128,…个元素上。因此,在n次调用Int32.MaxValue时,重新分配的总成本大约为4 + 8 + 16 + ... + n / 8 + n / 4 + n / 2,

下面是一个示例,显示了一系列加法操作中内部数组的状态:

Add

// ┌┐ var list = new List<char>(); // ││ Count: 0 // └┘ Capacity: 0 // ┌───┬───┬───┬───┐ list.Add('h'); // │ h │ ░ │ ░ │ ░ │ Count: 1 // └───┴───┴───┴───┘ Capacity: 4 // ┌───┬───┬───┬───┐ list.Add('e'); // │ h │ e │ ░ │ ░ │ Count: 2 // └───┴───┴───┴───┘ Capacity: 4 // ┌───┬───┬───┬───┐ list.Add('l'); // │ h │ e │ l │ ░ │ Count: 3 // └───┴───┴───┴───┘ Capacity: 4 // ┌───┬───┬───┬───┐ list.Add('l'); // │ h │ e │ l │ l │ Count: 4 // └───┴───┴───┴───┘ Capacity: 4 // ┌───┬───┬───┬───┬───┬───┬───┬───┐ list.Add('o'); // │ h │ e │ l │ l │ o │ ░ │ ░ │ ░ │ Count: 5 // └───┴───┴───┴───┴───┴───┴───┴───┘ Capacity: 8 // ┌───┬───┬───┬───┬───┬───┬───┬───┐ list.Add(' '); // │ h │ e │ l │ l │ o │ │ ░ │ ░ │ Count: 6 // └───┴───┴───┴───┴───┴───┴───┴───┘ Capacity: 8 // ┌───┬───┬───┬───┬───┬───┬───┬───┐ list.Add('w'); // │ h │ e │ l │ l │ o │ │ w │ ░ │ Count: 7 // └───┴───┴───┴───┴───┴───┴───┴───┘ Capacity: 8 // ┌───┬───┬───┬───┬───┬───┬───┬───┐ list.Add('o'); // │ h │ e │ l │ l │ o │ │ w │ o │ Count: 8 // └───┴───┴───┴───┴───┴───┴───┴───┘ Capacity: 8 // ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ list.Add('r'); // │ h │ e │ l │ l │ o │ │ w │ o │ r │ ░ │ ░ │ ░ │ ░ │ ░ │ ░ │ ░ │ Count: 9 // └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ Capacity: 16 // ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ list.Add('l'); // │ h │ e │ l │ l │ o │ │ w │ o │ r │ ░ │ ░ │ ░ │ ░ │ ░ │ ░ │ ░ │ Count: 10 // └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ Capacity: 16 // ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ list.Add('d'); // │ h │ e │ l │ l │ o │ │ w │ o │ r │ l │ d │ ░ │ ░ │ ░ │ ░ │ ░ │ Count: 11 // └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ Capacity: 16 符号表示仍未使用的已分配空间。这些数组位置将包含的{​​{3}}。对于T,这将是空字符char。但是,这些值永远不会对消费者可见。

通过default value将多个元素加在一起时,最多只能执行一次重新分配。如果将以前的容量增加一倍不足以容纳所有新元素,则将内部数组立即增加到新数量。

与添加不同,删除元素不会自动缩小列表。但是,您可以通过调用AddRange来手动完成此操作。

TrimExcess中所述,上述某些方面(例如默认初始容量4)是从.NET Framework 4.7.2的comments派生而来的实现细节。但是,核心原则根深蒂固,在其他/未来框架中不会改变。

答案 1 :(得分:3)

您的假设是正确的,编译器不分配任何内容。 from keras.callbacks import ModelCheckpoint from keras.models import Sequential from keras.layers import Dense import numpy as np # Subclass ModelCheckpoint class MyModelCheckpoint(ModelCheckpoint): def __init__(self, *args, **kwargs): super(MyModelCheckpoint, self).__init__(*args, **kwargs) # redefine the save so it only activates after 100 epochs def on_epoch_end(self, epoch, logs=None): if epoch > 100: super(MyModelCheckpoint, self).on_epoch_end(epoch, logs) # A simple example neural net model = Sequential() model.add(Dense(1, input_dim=5)) model.compile(loss='mse', optimizer='adam') # Toy dataset X = np.random.rand(5, 5) y = np.random.rand(5, 1) # Create checkpointer as you would with a regular ModelCheckpoint checkpointer = MyModelCheckpoint(filepath='{epoch}.h5') # Fit the model using it as a callback model.fit(X, y, callbacks=[checkpointer], verbose=1, epochs=200) 类在内部使用数组存储元素,并检查每次调用List<T>时数组的大小是否足够,如您所见in the source code

Add

答案 2 :(得分:3)

@CamiloTerevinto's answer中,源代码将是明确的,具体说明了如何实现此目标,但是文档也对此进行了说明。

Remarks section of the List<> class指出:

  

List类是ArrayList类的通用等效项。它通过使用其大小根据需要动态增加的数组来实现IList通用接口。

Remarks section of the Capacity property详细说明:

  

容量是需要调整大小之前List可以存储的元素数,而Count是List中实际存在的元素数。

     

容量始终大于或等于Count。如果在添加元素时Count超过了Capacity,则通过在复制旧元素和添加新元素之前自动重新分配内部数组来增加容量。