web-dev-qa-db-ja.com

リストの要素が別のリストにあるかどうかを確認するにはどうすればよいですか?

最初のリストの少なくとも1つの要素が2番目のリストにあるかどうかを知りたいです。

私はそれを行う2つの方法を見ることができます。私たちのリストが次のとおりだとしましょう:

List<string> list1 = new[] { "A", "C", "F", "H", "I" };
List<string> list2 = new[] { "B", "D", "F", "G", "I" };

最初のアプローチはループを使用します:

bool isFound = false;
foreach (item1 in list1)
{
    if (list2.Contains(item1))
    {
        isFound = true;
        break;
    }
}

2つ目は、Linqを直接使用します。

bool isFound = list1.Intersect(list2).Any();

最初のものは書くのが長く、あまり簡単で読みやすいものではありません。 2つ目は短く明確ですが、特に大きなリストではパフォーマンスが低下します。

それを行うためのエレガントな方法は何でしょうか?

24

2番目のものは、最初のものよりも大きなリストで優れたパフォーマンスを発揮します。 Intersectは、他のリストの要素のメンバーシップをチェックする前に、1つのリストの要素をハッシュテーブルに配置します。

22
mqp

オリジナルが明らかに(最悪の場合)O(n * m)である場合に、LINQのパフォーマンスを批判するのは奇妙に思えます。 LINQアプローチは私は期待していますリストでHashSet<T>を使用し、次にストリーミングイテレータブロックを使用します-したがって、パフォーマンスはO(n + m)である必要があります-つまり、より良いです。

9
Marc Gravell

大きなリストの場合、2番目の方が高速になると思います。最初のものはO(list1.Count * list2.Count)であるのに対し、2番目のものはO(list1.Count + list2.Count)です。ただし、2つ目はより多くのメモリを必要とします。

また、linqのオーバーヘッドは通常、手作りのコードに対する一定の乗算係数です。 2番目のコードは命令型コードよりも最大で2倍遅いと思いますが、おそらくそれでもないでしょう。線形パフォーマンスを維持しながらメモリ使用量を少なくするようにコードを注意深く記述すると、O(list1.Count+list2.Count)メモリを使用します。これはO(Min(list1,list2))に削減できます。

このコードは、大きなリストでは比較的高速である必要があります。

bool isFound = false;
HashSet<string> set2=new HashSet<string>(list2);
foreach (item1 in list1)
{
    if (set2.Contains(item1))
    {
        isFound = true;
        break;
    }
}

常にlist2を使用する代わりに、小さいリストをハッシュセットにすることで、このコードをさらに最適化できます。

6
CodesInChaos

受け入れられた答えは素晴らしいですが、Intersectのマッピングがないため、Linq-to-sqlでは機能しません。その場合は、以下を使用する必要があります。

bool isFound = table.Any(row => list2.Contains(row.FieldWithValue));

これはWHERE EXSITSにコンパイルされます

4

これは、あるリストの要素が別のリストに存在するかどうかを知る別の方法です。

bool present = List1.Any(t => List2.Any(y => y == t));
0
user11961350