2つの大規模な(> 50.000アイテム)を比較するための最も速い(そして最もリソースを消費しない)ものは何ですか?その結果、以下のような2つのリストがあります。
現在私はListまたはIReadOnlyCollectionを使って作業していて、この問題をlinqクエリで解決しています。
var list1 = list.Where(i => !list2.Contains(i)).ToList();
var list2 = list2.Where(i => !list.Contains(i)).ToList();
しかし、これは私が望むほどうまくは行かない。私はたくさんのリストを処理する必要があるので、これをより速くより少ないリソース集約的にするという考えはありますか?
Except
を使用してください。
var firstNotSecond = list1.Except(list2).ToList();
var secondNotFirst = list2.Except(list1).ToList();
これより実際にはわずかに速いアプローチがあると私は思いますが、これでもあなたのO(N * M)アプローチより速い非常に速いになります。
これらを組み合わせたい場合は、上記のメソッドを作成してからreturnステートメントを作成します。
return !firstNotSecond.Any() && !secondNotFirst.Any();
もっと効率的なのは Enumerable.Except
を使うことです。
var inListButNotInList2 = list.Except(list2);
var inList2ButNotInList = list2.Except(list);
このメソッドは、遅延実行を使用して実装されています。それはあなたが例えば書くことができることを意味します:
var first10 = inListButNotInList2.Take(10);
オブジェクトを比較するために内部的にSet<T>
を使用しているのでそれも効率的です。これは、最初に2番目のシーケンスからすべての異なる値を収集し、次に最初の結果をストリーミングして、前に見たことがないことを確認することによって機能します。
結果を大文字と小文字を区別しないにするには、次のようにします。
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が含まれます。
この問題ではありませんが、リストを等しいかどうかで比較するためのコードを次に示します。同一のオブジェクト
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);
}
この方法を試してください:
var difList = list1.Where(a => !list2.Any(a1 => a1.id == a.id))
.Union(list2.Where(a => !list1.Any(a1 => a1.id == a.id)));
このコードを使って、何百万ものレコードを持つ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;
}
Enumerable.SequenceEqualメソッド
等価比較子に従って、2つのシーケンスが等しいかどうかを判断します。 MS.Docs
Enumerable.SequenceEqual(list1, list2);
これはすべてのプリミティブデータ型に有効です。もしあなたがカスタムオブジェクトでそれを使う必要があるなら、IEqualityComparer
を実装する必要があります。
同等のためにオブジェクトの比較をサポートするためのメソッドを定義します。
IEqualityComparerインターフェイス
同等のためにオブジェクトの比較をサポートするためのメソッドを定義します。 IEqualityComparerのMS.Docs
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の記述方法に注意することは非常に重要です。 IEquatable
、Equals
、およびGetHashCode
mustを適切に実装するには、論理的に互換性のあるインスタンスのプロパティを操作します方法。
同じ内容の2つのリストは依然として異なるオブジェクトであり、異なるハッシュコードを生成します。これらの2つのリストを等しいものとして扱いたいので、GetHashCode
にそれぞれのリストに対して同じ値を生成させる必要があります。これを実現するには、ハッシュコードをリスト内のすべての要素に委任し、標準のビット単位のXORを使用してそれらをすべて結合します。 XORは順序に依存しないため、リストが異なる方法でソートされているかどうかは関係ありません。同等のメンバーのみが含まれていることが重要です。
注:奇妙な名前は、メソッドがリスト内の要素の順序を考慮しないという事実を意味することです。リスト内の要素の順序を気にする場合、このメソッドはあなたのためではありません!
結合結果だけが必要な場合は、これも機能します。
var set1 = new HashSet<T>(list1);
var set2 = new HashSet<T>(list2);
var areEqual = set1.SetEquals(set2);
ここで、Tはリスト要素の型です。