LinqのUnionVs Concat


86

とについて質問がUnionありConcatます。の場合、両方とも同じように動作していると思いList<T>ます。

var a1 = (new[] { 1, 2 }).Union(new[] { 1, 2 });             // O/P : 1 2
var a2 = (new[] { 1, 2 }).Concat(new[] { 1, 2 });            // O/P : 1 2 1 2

var a3 = (new[] { "1", "2" }).Union(new[] { "1", "2" });     // O/P : "1" "2"
var a4 = (new[] { "1", "2" }).Concat(new[] { "1", "2" });    // O/P : "1" "2" "1" "2"

上記の結果が期待されます、

しかし、List<T>私が同じ結果を得ている場合。

class X
{
    public int ID { get; set; }
}

class X1 : X
{
    public int ID1 { get; set; }
}

class X2 : X
{
    public int ID2 { get; set; }
}

var lstX1 = new List<X1> { new X1 { ID = 10, ID1 = 10 }, new X1 { ID = 10, ID1 = 10 } };
var lstX2 = new List<X2> { new X2 { ID = 10, ID2 = 10 }, new X2 { ID = 10, ID2 = 10 } };

var a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>());     // O/P : a5.Count() = 4
var a6 = lstX1.Cast<X>().Concat(lstX2.Cast<X>());    // O/P : a6.Count() = 4

ただし、の場合は両方とも同じように動作しList<T>ます。

何か提案がありますか?


1
これら2つの方法の違いを知っているなら、なぜ結果があなたを驚かせるのですか?これは、メソッドの機能の直接的な結果です。
Konrad Rudolph

@KonradRudolph、つまりList <T>の場合、「Union」/「Concat」のいずれか1つを使用できます。どちらも同じように動作しているからです。
Prasad Kanaparthi 2012年

いいえ、明らかに違います。最初の例が示すように、これらは同じように動作しません。
Konrad Rudolph

あなたの例では、すべてのIDが異なります。
ジムミッシェル2012年

@JimMischel、私の投稿を編集しました。同じ値でも同じように動作します。
Prasad Kanaparthi 2012年

回答:


110

UnionはDistinct値を返します。デフォルトでは、アイテムの参照を比較します。あなたのアイテムは異なる参照を持っているので、それらはすべて異なると見なされます。基本型Xにキャストしても、参照は変更されません。

EqualsおよびGetHashCode(個別のアイテムを選択するために使用される)をオーバーライドする場合、アイテムは参照によって比較されません。

class X
{
    public int ID { get; set; }

    public override bool Equals(object obj)
    {
        X x = obj as X;
        if (x == null)
            return false;
        return x.ID == ID;
    }

    public override int GetHashCode()
    {
        return ID.GetHashCode();
    }
}

ただし、すべてのアイテムの値は異なりますID。したがって、すべてのアイテムはまだ異なると見なされます。あなたは同じで、いくつかのアイテムを提供する場合はID、あなたは違いが表示されますUnionとをConcat

var lstX1 = new List<X1> { new X1 { ID = 1, ID1 = 10 }, 
                           new X1 { ID = 10, ID1 = 100 } };
var lstX2 = new List<X2> { new X2 { ID = 1, ID2 = 20 }, // ID changed here
                           new X2 { ID = 20, ID2 = 200 } };

var a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>());  // 3 distinct items
var a6 = lstX1.Cast<X>().Concat(lstX2.Cast<X>()); // 4

整数は値型であり、値によって比較されるため、最初のサンプルは機能します。


3
参照を比較していなくても、たとえば内部のIDを比較していても、IDが異なるため、4つの項目があります。
ローリング2012年

@Swaniいいえ、そうではありません。上で述べたように、2番目のコレクションの最初のアイテムのIDを変更しなかったと思います
Sergey Berezovskiy 2012年

@Swaniでは、前述のように、EqualsとGetHashCodeをオーバーライドしていません
Sergey Berezovskiy

@lazyberezovsky、私はあなたの答えに同意します。しかし、私はまだコメントに満足していません。サンプルコードを実行すると、「a5」と「a6」で同じ結果が表示されます。私は解決策を探していません。しかし、なぜ「コンキャット」と「ユニオン」がその主張で同じように振る舞うのか。返信してください。
Prasad Kanaparthi 2012年

3
@Swani申し訳ありませんが、afkでした。x.Union(y)と同じx.Concat(y).Distinct()です。したがって、違いは適用することだけDistinctです。Linqは、連結されたシーケンスで異なる(つまり異なる)オブジェクトをどのように選択しますか?サンプルコード(質問から)では、Linqは参照(つまりメモリ内のアドレス)によってオブジェクトを比較します。new演算子を使用して新しいオブジェクトを作成すると、新しいアドレスにメモリが割り当てられます。したがって、新しく作成されたオブジェクトが4つある場合、アドレスは異なります。そして、すべてのオブジェクトが区別されます。したがってDistinct、シーケンスからすべてのオブジェクトを返します。
Sergey Berezovskiy 2012年

48

Concat文字通り、最初のシーケンスのアイテムを返し、その後に2番目のシーケンスのアイテムを返します。Concat2つの2項目シーケンスで使用する場合、常に4項目シーケンスを取得します。

Union基本的に。がConcat続きDistinctます。

最初の2つのケースでは、入力シーケンスの各ペアに正確に2つの異なるアイテムがあるため、2アイテムのシーケンスになります。

3番目のケースでは、2つの入力シーケンスの4つのアイテムすべてが異なるため、4アイテムのシーケンスになります


14

Unionそして、Concat同じように動作するのでUnion、カスタムすることなく、重複を検出することはできませんIEqualityComparer<X>。両方が同じ参照であるかどうかを調べているだけです。

public class XComparer: IEqualityComparer<X>
{
    public bool Equals(X x1, X x2)
    {
        if (object.ReferenceEquals(x1, x2))
            return true;
        if (x1 == null || x2 == null)
            return false;
        return x1.ID.Equals(x2.ID);
    }

    public int GetHashCode(X x)
    {
        return x.ID.GetHashCode();
    }
}

これで、次の過負荷で使用できますUnion

var comparer = new XComparer();
a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>(), new XComparer()); 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.