Union Vs Concat à Linq

86

J'ai une question sur Unionet Concat. Je suppose que les deux se comportent de la même manière en cas de 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"

Les résultats ci-dessus sont attendus,

Mais au cas où j'obtiens le List<T>même résultat.

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

Mais les deux se comportent de la même manière List<T>.

Des suggestions s'il vous plaît?

Prasad Kanaparthi
la source
1
Si vous connaissez la différence entre ces deux méthodes, pourquoi le résultat vous surprend-il? C'est une conséquence directe de la fonctionnalité des méthodes.
Konrad Rudolph
@KonradRudolph, Ce que je veux dire, c'est qu'en cas de liste <T>, je peux utiliser n'importe quel 'Union' / 'Concat'. Parce que les deux se comportent de la même manière.
Prasad Kanaparthi
Non, évidemment non. Ils ne se comportent pas de la même manière, comme le montre votre premier exemple.
Konrad Rudolph
Dans votre exemple, tous les identifiants sont différents.
Jim Mischel
@JimMischel, a édité mon message. même avec les mêmes valeurs, il se comporte de la même manière.
Prasad Kanaparthi

Réponses:

110

Union renvoie des Distinctvaleurs. Par défaut, il comparera les références des articles. Vos articles ont des références différentes, ils sont donc tous considérés comme différents. Lorsque vous transtypez en type de base X, la référence n'est pas modifiée.

Si vous remplacez Equalset GetHashCode(utilisé pour sélectionner des éléments distincts), les éléments ne seront pas comparés par référence:

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

Mais tous vos articles ont une valeur différente de ID. Donc, tous les éléments sont toujours considérés comme différents. Si vous fournissez plusieurs éléments avec le même, IDvous verrez la différence entre Unionet 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

Votre exemple initial fonctionne, car les entiers sont des types valeur et ils sont comparés par valeur.

Sergey Berezovskiy
la source
3
Même s'il ne comparait pas les références mais par exemple les identifiants à l'intérieur, il y aurait toujours quatre éléments car les identifiants sont différents.
Rawling
@Swani non, ils ne le sont pas. Je pense que vous n'avez pas changé l'ID du premier élément de la deuxième collection, comme je l'ai indiqué ci
Sergey Berezovskiy
@Swani alors vous n'avez pas remplacé Equals et GetHashCode, comme je l'ai dit ci
Sergey Berezovskiy
@lazyberezovsky, je suis d'accord avec votre réponse. Mais je ne suis toujours pas satisfait des commentaires. Si vous exécutez mon exemple de code, vous pouvez voir le même résultat pour «a5» et «a6». Je ne cherche pas de solution. Mais pourquoi «Concat» et «Union» se comportent-ils de la même façon à cette époque. Répondez, s'il vous plaît.
Prasad Kanaparthi
3
@Swani désolé, c'était pas grave. x.Union(y)est le même que x.Concat(y).Distinct(). Donc, la différence ne concerne que l'application Distinct. Comment Linq sélectionne des objets distincts (c'est-à-dire différents) dans des séquences concaténées? Dans votre exemple de code (à partir de la question), Linq compare les objets par référence (c'est-à-dire adresse en mémoire). Lorsque vous créez un nouvel objet via l' newopérateur, il alloue de la mémoire à une nouvelle adresse. Ainsi, lorsque vous avez quatre nouveaux objets créés, les adresses seront différentes. Et tous les objets seront distincts. Ainsi Distinctretournera tous les objets de la séquence.
Sergey Berezovskiy
48

Concatrenvoie littéralement les éléments de la première séquence suivis des éléments de la deuxième séquence. Si vous utilisez Concatdeux séquences de 2 éléments, vous obtiendrez toujours une séquence de 4 éléments.

Unionest essentiellement Concatsuivi de Distinct.

Dans vos deux premiers cas, vous vous retrouvez avec des séquences de 2 éléments car, entre eux, chaque paire de séquences d'entrée a exactement deux éléments distincts.

Dans votre troisième cas, vous vous retrouvez avec une séquence de 4 éléments car les quatre éléments de vos deux séquences d'entrée sont distincts .

Rawling
la source
14

Unionet Concatse comportent de la même manière car Unionne peuvent pas détecter les doublons sans une coutume IEqualityComparer<X>. Il s'agit simplement de voir si les deux sont la même référence.

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

Vous pouvez maintenant l'utiliser dans la surcharge de Union:

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