web-dev-qa-db-ja.com

2つのリストを比較する最も簡単な方法<>

2つの大規模な(> 50.000アイテム)を比較するための最も速い(そして最もリソースを消費しない)ものは何ですか?その結果、以下のような2つのリストがあります。

  1. 最初のリストには表示されるが2番目のリストには表示されない項目
  2. 2番目のリストには表示されるが最初のリストには表示されない項目

現在私はListまたはIReadOnlyCollectionを使って作業していて、この問題をlinqクエリで解決しています。

var list1 = list.Where(i => !list2.Contains(i)).ToList();
var list2 = list2.Where(i => !list.Contains(i)).ToList();

しかし、これは私が望むほどうまくは行かない。私はたくさんのリストを処理する必要があるので、これをより速くより少ないリソース集約的にするという考えはありますか?

175
Frank

Except を使用してください。

var firstNotSecond = list1.Except(list2).ToList();
var secondNotFirst = list2.Except(list1).ToList();

これより実際にはわずかに速いアプローチがあると私は思いますが、これでもあなたのO(N * M)アプローチより速い非常に速いになります。

これらを組み合わせたい場合は、上記のメソッドを作成してからreturnステートメントを作成します。

return !firstNotSecond.Any() && !secondNotFirst.Any();
372
Jon Skeet

もっと効率的なのは Enumerable.Except を使うことです。

var inListButNotInList2 = list.Except(list2);
var inList2ButNotInList = list2.Except(list);

このメソッドは、遅延実行を使用して実装されています。それはあなたが例えば書くことができることを意味します:

var first10 = inListButNotInList2.Take(10);

オブジェクトを比較するために内部的にSet<T>を使用しているのでそれも効率的です。これは、最初に2番目のシーケンスからすべての異なる値を収集し、次に最初の結果をストリーミングして、前に見たことがないことを確認することによって機能します。

35
Tim Schmelter

結果を大文字と小文字を区別しないにするには、次のようにします。

List<string> list1 = new List<string> { "a.dll", "b1.dll" };
List<string> list2 = new List<string> { "A.dll", "b2.dll" };

var firstNotSecond = list1.Except(list2, StringComparer.OrdinalIgnoreCase).ToList();
var secondNotFirst = list2.Except(list1, StringComparer.OrdinalIgnoreCase).ToList();

firstNotSecondにはb1.dllが含まれます。

secondNotFirstにはb2.dllが含まれます。

8
e.gad

この問題ではありませんが、リストを等しいかどうかで比較するためのコードを次に示します。同一のオブジェクト

public class EquatableList<T> : List<T>, IEquatable<EquatableList<T>> where    T : IEquatable<T>

/// <summary>
/// True, if this contains element with equal property-values
/// </summary>
/// <param name="element">element of Type T</param>
/// <returns>True, if this contains element</returns>
public new Boolean Contains(T element)
{
    return this.Any(t => t.Equals(element));
}

/// <summary>
/// True, if list is equal to this
/// </summary>
/// <param name="list">list</param>
/// <returns>True, if instance equals list</returns>
public Boolean Equals(EquatableList<T> list)
{
    if (list == null) return false;
    return this.All(list.Contains) && list.All(this.Contains);
}
5
Pius Hermit

この方法を試してください:

var difList = list1.Where(a => !list2.Any(a1 => a1.id == a.id))
            .Union(list2.Where(a => !list1.Any(a1 => a1.id == a.id)));
2
Ali Issa

このコードを使って、何百万ものレコードを持つ2つのリストを比較しました。

この方法はそれほど時間がかかりません

    //Method to compare two list of string
    private List<string> Contains(List<string> list1, List<string> list2)
    {
        List<string> result = new List<string>();

        result.AddRange(list1.Except(list2, StringComparer.OrdinalIgnoreCase));
        result.AddRange(list2.Except(list1, StringComparer.OrdinalIgnoreCase));

        return result;
    }
1
Sathish

Enumerable.SequenceEqualメソッド

等価比較子に従って、2つのシーケンスが等しいかどうかを判断します。 MS.Docs

Enumerable.SequenceEqual(list1, list2);

これはすべてのプリミティブデータ型に有効です。もしあなたがカスタムオブジェクトでそれを使う必要があるなら、IEqualityComparerを実装する必要があります。

同等のためにオブジェクトの比較をサポートするためのメソッドを定義します。

IEqualityComparerインターフェイス

同等のためにオブジェクトの比較をサポートするためのメソッドを定義します。 IEqualityComparerのMS.Docs

1
miguelmpn
using System.Collections.Generic;
using System.Linq;

namespace YourProject.Extensions
{
    public static class ListExtensions
    {
        public static bool SetwiseEquivalentTo<T>(this List<T> list, List<T> other)
            where T: IEquatable<T>
        {
            if (list.Except(other).Any())
                return false;
            if (other.Except(list).Any())
                return false;
            return true;
        }
    }
}

if2つのリストが異なるだけで、それらの違いを知る必要がない場合があります。その場合、この拡張メソッドをプロジェクトに追加することを検討してください。リストされたオブジェクトはIEquatableを実装する必要があることに注意してください!

使用法:

public sealed class Car : IEquatable<Car>
{
    public Price Price { get; }
    public List<Component> Components { get; }

    ...
    public override bool Equals(object obj)
        => obj is Car other && Equals(other);

    public bool Equals(Car other)
        => Price == other.Price
            && Components.SetwiseEquivalentTo(other.Components);

    public override int GetHashCode()
        => Components.Aggregate(
            Price.GetHashCode(),
            (code, next) => code ^ next.GetHashCode()); // Bitwise XOR
}

Componentクラスが何であっても、ここに示されているCarのメソッドはほぼ同じように実装する必要があります。

GetHashCodeの記述方法に注意することは非常に重要です。 IEquatableEquals、およびGetHashCodemustを適切に実装するには、論理的に互換性のあるインスタンスのプロパティを操作します方法。

同じ内容の2つのリストは依然として異なるオブジェクトであり、異なるハッシュコードを生成します。これらの2つのリストを等しいものとして扱いたいので、GetHashCodeにそれぞれのリストに対して同じ値を生成させる必要があります。これを実現するには、ハッシュコードをリスト内のすべての要素に委任し、標準のビット単位のXORを使用してそれらをすべて結合します。 XORは順序に依存しないため、リストが異なる方法でソートされているかどうかは関係ありません。同等のメンバーのみが含まれていることが重要です。

注:奇妙な名前は、メソッドがリスト内の要素の順序を考慮しないという事実を意味することです。リスト内の要素の順序を気にする場合、このメソッドはあなたのためではありません!

1
Devon Parsons

結合結果だけが必要な場合は、これも機能します。

var set1 = new HashSet<T>(list1);
var set2 = new HashSet<T>(list2);
var areEqual = set1.SetEquals(set2);

ここで、Tはリスト要素の型です。