web-dev-qa-db-ja.com

List <T> .RemoveAll()効率/コンパイラの最適化

効率に関しては、コンパイラがnotを含む配列を作成して1, 3, 5次のコードのループの反復ごとに?

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

私は読みやすさのためにそれを好みますが、パフォーマンスのためではありません。

24
maxp

答えは、配列の割り当てを最適化しないということではありません

基本的に、述語が呼び出されるたびに、コンパイラが生成したクラスと照合して、新しい配列を初期化し、Containsを呼び出します( here を参照)。

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

@Michael Randallがすでに書いたように、それは不可能のようです。

質問のコードは読みやすく、RemoveAllメソッドにリストがあることに同意します。しかし、インスタンスを一度だけ持つために、私はそれを行うための3つのアイデアを持っています。

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

これは実際にはあなたのものであり、外部変数を必要とすることの明白さはほとんどありません。

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

これは実際にはLinqを使用したかなり良い解決策です。

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


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

これは私がすることです。ほぼすべてのコードで、foreachの必要性を回避するために、拡張メソッドを "反復"しています。また、.ForEach(..)を作成するために常にすべてを「toList」したくありません。

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

コンパイラーはそれほど賢くないので、私たちは彼よりも優れている必要があります。

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