Union gegen Concat in Linq

85

Ich habe eine Frage zu Unionund Concat. Ich denke, beide verhalten sich im Falle von gleich 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"

Das obige Ergebnis wird erwartet,

Aber falls List<T>ich das gleiche Ergebnis bekomme .

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

Aber beide verhalten sich gleich List<T>.

Irgendwelche Vorschläge bitte?

Prasad Kanaparthi
quelle
1
Wenn Sie den Unterschied zwischen diesen beiden Methoden kennen, warum überrascht Sie das Ergebnis? Dies ist eine direkte Folge der Funktionalität der Methoden.
Konrad Rudolph
@KonradRudolph, was ich meine, ist, falls Liste <T> ich kann jede 'Union' / 'Concat' verwenden. Weil sich beide gleich verhalten.
Prasad Kanaparthi
Nein, offensichtlich nicht. Sie verhalten sich nicht gleich, wie Ihr erstes Beispiel zeigt.
Konrad Rudolph
In Ihrem Beispiel sind alle IDs unterschiedlich.
Jim Mischel
@ JimMischel, habe meinen Beitrag bearbeitet. Auch bei gleichen Werten verhält es sich gleich.
Prasad Kanaparthi

Antworten:

109

Union gibt DistinctWerte zurück. Standardmäßig werden Referenzen von Elementen verglichen. Ihre Artikel haben unterschiedliche Referenzen, daher werden sie alle als unterschiedlich angesehen. Wenn Sie in einen Basistyp umwandeln X, wird die Referenz nicht geändert.

Wenn Sie überschreiben Equalsund GetHashCode(zur Auswahl bestimmter Elemente verwendet), werden Elemente nicht anhand der folgenden Referenz verglichen:

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();
    }
}

Aber alle Ihre Artikel haben einen unterschiedlichen Wert von ID. Also alle Gegenstände immer noch als unterschiedlich angesehen. Wenn Sie mehrere Artikel mit demselben versehen, sehen IDSie einen Unterschied zwischen Unionund 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

Ihr erstes Beispiel funktioniert, da Ganzzahlen Werttypen sind und nach Wert verglichen werden.

Sergey Berezovskiy
quelle
3
Selbst wenn keine Referenzen verglichen würden, sondern z. B. die darin enthaltenen IDs, gäbe es immer noch vier Elemente, da die IDs unterschiedlich sind.
Rawling
@ Swani nein, das sind sie nicht. Ich denke, Sie haben die ID des ersten Artikels in der zweiten Sammlung nicht geändert, wie oben erwähnt
Sergey Berezovskiy
@ Swani dann haben Sie Equals und GetHashCode nicht überschrieben, wie ich oben sagte
Sergey Berezovskiy
@lazyberezovsky, ich stimme Ihrer Antwort zu. Aber ich bin immer noch nicht zufrieden mit den Kommentaren. Wenn Sie meinen Beispielcode ausführen, sehen Sie das gleiche Ergebnis für 'a5' und 'a6'. Ich suche keine Lösung. Aber warum verhalten sich "Concat" und "Union" bei dieser Situation gleich? Bitte antworte.
Prasad Kanaparthi
3
@ Swani sorry, war afk. x.Union(y)ist das gleiche wie x.Concat(y).Distinct(). Der Unterschied besteht also nur in der Bewerbung Distinct. Wie wählt Linq unterschiedliche (dh unterschiedliche) Objekte in verketteten Sequenzen aus? In Ihrem Beispielcode (aus Frage) vergleicht Linq Objekte anhand der Referenz (dh der Adresse im Speicher). Wenn Sie ein neues Objekt über den newOperator erstellen , weist es Speicher an einer neuen Adresse zu. Wenn Sie also vier neu erstellte Objekte haben, sind die Adressen unterschiedlich. Und alle Objekte werden unterschiedlich sein. Somit Distinctwerden alle Objekte aus der Sequenz zurückgegeben.
Sergey Berezovskiy
48

ConcatGibt buchstäblich die Elemente aus der ersten Sequenz zurück, gefolgt von den Elementen aus der zweiten Sequenz. Wenn Sie Concatzwei Sequenzen mit 2 Elementen verwenden, erhalten Sie immer eine Sequenz mit 4 Elementen.

Unionwird im Wesentlichen Concatgefolgt von Distinct.

In Ihren ersten beiden Fällen erhalten Sie Sequenzen mit zwei Elementen, da zwischen ihnen jedes Paar von Eingabequadraten genau zwei unterschiedliche Elemente enthält.

In Ihrem dritten Fall erhalten Sie eine Sequenz mit 4 Elementen, da alle vier Elemente in Ihren beiden Eingabesequenzen unterschiedlich sind .

Rawling
quelle
14

Unionund Concatverhalten sich gleich, da UnionDuplikate ohne Custom nicht erkannt werden können IEqualityComparer<X>. Es wird nur gesucht, ob beide dieselbe Referenz sind.

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();
    }
}

Jetzt können Sie es in der Überladung von verwenden Union:

var comparer = new XComparer();
a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>(), new XComparer()); 
Tim Schmelter
quelle