web-dev-qa-db-fra.com

Liste <T> .RemoveAll () optimisation efficacité / compilateur

En ce qui concerne l'efficacité, quelqu'un sait-il si le compilateur est assez intelligent pour pas créer le tableau contenant 1, 3, 5 pour chaque itération de la boucle dans le code suivant?

var foo = new List<int> { 1, 2, 3, 4, 5 };
foo.RemoveAll(i => new[] { 1, 3, 5 }.Contains(i));

Je le préfère pour la lisibilité, mais pas pour des raisons de performances.

24
maxp

La réponse est non, elle n'optimise pas l'allocation du tableau

Fondamentalement, chaque fois que le prédicat est appelé, il vérifie la classe générée par le compilateur et initialise un nouveau tableau pour appeler le Contains (comme vous pouvez le voir ici )

private sealed class <>c
{
    public static readonly <>c <>9 = new <>c();

    public static Predicate<int> <>9__0_0;

    internal bool <M>b__0_0(int i)
    {
        // bam!
        int[] obj = new int[3];
        RuntimeHelpers.InitializeArray(obj, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
        return Enumerable.Contains(obj, i);
    }
}
13
Michael Randall

Comme @Michael Randall l'a déjà écrit, il semble que ce ne soit pas possible.

Je suis d'accord, que votre code en question est bien lisible, ayant la liste dans la méthode RemoveAll. Mais pour avoir l'instance une seule fois, j'ai trois idées pour le faire:

int[] a = null;
foo.RemoveAll(i => (a ?? (a = new[] { 1, 3, 5 })).Contains(i));

Ceci est en fait le vôtre, avec peu d'impatience d'avoir besoin d'une variable externe.

 foo = foo.Except(new[] { 1, 3, 5 }).ToList();

C'est en fait une solution plutôt agréable avec Linq.

 new List<int>{1, 3, 5}.ForEach(x => foo.Remove(x));


 new[] {1, 3, 5}.Iterate(x => foo.Remove(x));

C'est quelque chose que je ferais. Dans presque tout mon code, j'ai ma méthode d'extension "Itérer" pour éviter le besoin de foreach. Et aussi, je ne veux pas tout "toList" tout le temps pour faire un .ForEach (..)

static class Extensions
{
    public static void Iterate<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
    {
        foreach (var item in source)
        {
            action.Invoke(item);
        }
    }
}
4
Malior

Puisque le compilateur n'est pas si intelligent, nous devons le surpasser.

var foo = new List<int> { 1, 2, 3, 4, 5 };
var bar = new HashSet<int>() { 1, 3, 5 };
foo.RemoveAll(i => bar.Contains(i));
0
Theodor Zoulias