Posso acrescentar um pouco mais à tua resposta! Mas minha contribuição vai ser exclusivamente acerca do C#.NET
Primeiro, nem toda classe que implementa a ICollection<T>
utilizará um array. Mas o List<T>
, que com certeza é a mais utilizada, usa um array em sua implementação.
Segundo, a eurística implementada em duas partes do código da List<T>
. Os códigos que mostrarei aqui estão no repositório do .NET no GitHub, aqui o link pro arquivo de implementação da List<T>
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs.
A primeira parte está no método List<T>.Grow()
, que é chamado sempre que a lista excede a capacidade anterior. É possível ver que a eurística é dobrar a capacidade até alcançar o patamar máximo que é Array.MaxLength
.
/// <summary>
/// Increase the capacity of this list to at least the specified <paramref name="capacity"/>.
/// </summary>
/// <param name="capacity">The minimum capacity to ensure.</param>
internal void Grow(int capacity)
{
Debug.Assert(_items.Length < capacity);
int newCapacity = _items.Length == 0 ? DefaultCapacity : 2 * _items.Length;
// Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
// Note that this check works even when _items.Length overflowed thanks to the (uint) cast
if ((uint)newCapacity > Array.MaxLength) newCapacity = Array.MaxLength;
// If the computed capacity is still less than specified, set to the original argument.
// Capacities exceeding Array.MaxLength will be surfaced as OutOfMemoryException by Array.Resize.
if (newCapacity < capacity) newCapacity = capacity;
Capacity = newCapacity;
}
A segunda parte está implementada na propriedade List<T>.Capacity
que é modificada no método List<T>.Grow()
, onde é implementada toda a lógica de criação do novo array e cópia dos elementos do antigo pro novo. Não é feita a liberação do antigo array porque isso é feito pelo GC.
// Gets and sets the capacity of this list. The capacity is the size of
// the internal array used to hold items. When set, the internal
// array of the list is reallocated to the given capacity.
//
public int Capacity
{
get => _items.Length;
set
{
if (value < _size)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
}
if (value != _items.Length)
{
if (value > 0)
{
T[] newItems = new T[value];
if (_size > 0)
{
Array.Copy(_items, newItems, _size);
}
_items = newItems;
}
else
{
_items = s_emptyArray;
}
}
}
}