Union Vs Concat a Linq


86

Ho una domanda su Unione Concat. Immagino che entrambi si comportino allo stesso modo in caso di 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"

Il risultato di cui sopra è previsto,

Ma nel caso in cui List<T>ottengo lo stesso risultato.

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

Ma entrambi si comportano allo stesso modo in caso di List<T>.

Qualche suggerimento per favore?


1
Se conosci la differenza tra questi due metodi, perché il risultato ti sorprende? È una diretta conseguenza della funzionalità dei metodi.
Konrad Rudolph

@KonradRudolph, Quello che voglio dire è che in caso di List <T> posso usare qualsiasi 'Union' / 'Concat'. Perché entrambi si comportano allo stesso modo.
Prasad Kanaparthi

No, ovviamente no. Non si comportano allo stesso modo, come mostra il tuo primo esempio.
Konrad Rudolph

Nel tuo esempio, tutti gli ID sono diversi.
Jim Mischel

@ JimMischel, modificato il mio post. anche con gli stessi valori si comporta allo stesso modo.
Prasad Kanaparthi

Risposte:


110

L'unione restituisce Distinctvalori. Per impostazione predefinita, confronterà i riferimenti degli articoli. I tuoi articoli hanno riferimenti diversi, quindi sono tutti considerati diversi. Quando si esegue il cast al tipo di base X, il riferimento non viene modificato.

Se sostituirai Equalse GetHashCode(utilizzato per selezionare elementi distinti), gli elementi non verranno confrontati per riferimento:

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

Ma tutti i tuoi articoli hanno un valore diverso di ID. Quindi tutti gli elementi sono ancora considerati diversi. Se fornirai più articoli con lo stesso ID, vedrai la differenza tra Unione 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

Il tuo campione iniziale funziona, perché i numeri interi sono tipi di valore e vengono confrontati per valore.


3
Anche se non si confrontavano i riferimenti ma, ad esempio, gli ID all'interno, ci sarebbero comunque quattro elementi poiché gli ID sono diversi.
Rawling

@Swani no, non lo sono. Penso che tu non abbia cambiato l'ID del primo oggetto nella seconda collezione, come ho affermato sopra
Sergey Berezovskiy

@Swani allora non hai sovrascritto Equals e GetHashCode, come ho affermato sopra
Sergey Berezovskiy

@lazyberezovsky, sono d'accordo con la tua risposta. Ma non sono ancora soddisfatto dei commenti. Se esegui il mio codice di esempio, puoi vedere lo stesso risultato per "a5" e "a6". Non sto cercando una soluzione. Ma perché "Concat" e "Union" si comportano allo stesso modo in quella situazione. Per favore rispondi.
Prasad Kanaparthi

3
@Swani scusa, era afk. x.Union(y)è lo stesso di x.Concat(y).Distinct(). Quindi la differenza è solo con l'applicazione Distinct. In che modo Linq seleziona oggetti distinti (cioè diversi) in sequenze concatenate? Nel tuo codice di esempio (dalla domanda) Linq confronta gli oggetti per riferimento (cioè l'indirizzo in memoria). Quando crei un nuovo oggetto tramite newoperatore, alloca la memoria al nuovo indirizzo. Quindi, quando hai quattro nuovi oggetti creati, gli indirizzi saranno diversi. E tutti gli oggetti saranno distinti. Così Distinctrestituirà tutti gli oggetti dalla sequenza.
Sergey Berezovskiy

48

Concatrestituisce letteralmente gli elementi della prima sequenza seguiti dagli elementi della seconda sequenza. Se usi Concatdue sequenze di 2 elementi, otterrai sempre una sequenza di 4 elementi.

Unionè essenzialmente Concatseguito da Distinct.

Nei tuoi primi due casi, ti ritroverai con sequenze di 2 elementi perché, tra di loro, ogni coppia di squenze di input ha esattamente due elementi distinti.

Nel terzo caso, si finisce con una sequenza di 4 elementi perché tutti e quattro gli elementi nelle due sequenze di input sono distinti .


14

Unione si Concatcomportano allo stesso modo poiché Unionnon è possibile rilevare i duplicati senza un'usanza IEqualityComparer<X>. Sta solo cercando se entrambi sono lo stesso riferimento.

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

Ora puoi usarlo nel sovraccarico di Union:

var comparer = new XComparer();
a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>(), new XComparer()); 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.