62 votes

Pourquoi est-AddRange plus rapide que d'utiliser une boucle foreach?

var fillData = new List<int>();
for (var i = 0; i < 100000; i++)
{
     fillData.Add(i);
}

var stopwatch1 = new Stopwatch();
stopwatch1.Start();
var autoFill = new List<int>();
autoFill.AddRange(fillData);
stopwatch1.Stop();

var stopwatch2 = new Stopwatch();
stopwatch2.Start();
var manualFill = new List<int>();
foreach (var i in fillData)
{
    manualFill.Add(i);
}
stopwatch2.Stop();

Quand je prends 4 résultats d' stopwach1 et stopwach2, stopwatch1 a toujours une valeur inférieure à stopwatch2. Cela signifie que addrange est toujours plus rapide qu' foreach. Quelqu'un sait-il pourquoi?

93voto

Jon Skeet Points 692016

Potentiellement, AddRange pouvez vérifier si la valeur passée à il met en oeuvre IList ou IList<T>. Si elle le fait, il peut savoir comment beaucoup de valeurs sont dans la gamme, et ainsi de combien d'espace il a besoin d'allouer... alors que l' foreach boucle peut-être besoin de réaffecter plusieurs fois.

En outre, même après l'attribution, List<T> pouvez utiliser IList<T>.CopyTo pour effectuer une copie en bloc dans le tableau (pour les plages de mettre en oeuvre IList<T>, bien sûr.)

Je soupçonne que vous trouverez que si vous essayez de votre test de nouveau, mais à l'aide d' Enumerable.Range(0, 100000) pour fillData au lieu de List<T>, les deux vont prendre à la même heure.

63voto

Marc Gravell Points 482669

Si vous utilisez Add, c'est le redimensionnement de l'intérieur de la matrice de progressivement selon les besoins (doublé), à partir du démarrage par défaut de la taille de 10 (IIRC). Si vous utilisez:

var manualFill = new List<int>(fillData.Count);

J'attends qu'il va changer radicalement (pas plus redimensionne / copie des données).

De réflecteur, AddRange est-ce à l'interne, plutôt que de l'expansion dans le doublement de:

ICollection<T> is2 = collection as ICollection<T>;
if (is2 != null)
{
    int count = is2.Count;
    if (count > 0)
    {
        this.EnsureCapacity(this._size + count);
        // ^^^ this the key bit, and prevents slow growth when possible ^^^

20voto

Kirill Polishchuk Points 28058

Parce qu' AddRange vérifie la taille des éléments ajoutés et augmente la taille du tableau interne qu'une seule fois.

7voto

Chamindu Points 60

Le dissassembly de réflecteur pour la Liste AddRange méthode a le code suivant

ICollection<T> is2 = collection as ICollection<T>;
if (is2 != null)
{
    int count = is2.Count;
    if (count > 0)
    {
        this.EnsureCapacity(this._size + count);
        if (index < this._size)
        {
            Array.Copy(this._items, index, this._items, index + count, this._size - index);
        }
        if (this == is2)
        {
            Array.Copy(this._items, 0, this._items, index, index);
            Array.Copy(this._items, (int) (index + count), this._items, (int) (index * 2), (int) (this._size - index));
        }
        else
        {
            T[] array = new T[count];
            is2.CopyTo(array, 0);
            array.CopyTo(this._items, index);
        }
        this._size += count;
    }
}

Comme vous pouvez le voir il y a quelques optimisations comme EnsureCapacity() l'appel et à l'aide du Tableau.Copier().

5voto

juergen d Points 85039

Lors de l'utilisation d' AddRange de la Collection peuvent augmenter la taille de la matrice, une fois puis copier les valeurs en elle.

À l'aide d'un foreach déclaration de la collecte des besoins d'augmentation de la taille de la collection, plus d'une fois.

L'augmentation de thr taille signifie une copie de l'ensemble de la gamme qui prend du temps.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X