Union Vs Concat di Linq

86

Saya memiliki pertanyaan tentang Uniondan Concat. Saya kira keduanya berperilaku sama dalam kasus 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"

Hasil di atas diharapkan,

Tapi Memetikan List<T>saya mendapatkan hasil yang sama.

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

Tapi keduanya berperilaku sama List<T>.

Ada saran, tolong?

Prasad Kanaparthi
sumber
1
Jika Anda mengetahui perbedaan antara kedua metode ini, mengapa hasilnya mengejutkan Anda? Ini adalah konsekuensi langsung dari fungsionalitas metode.
Konrad Rudolph
@KonradRudol, Yang saya maksud adalah memetikan Daftar <T> saya dapat menggunakan salah satu 'Union' / 'Concat'. Karena keduanya berperilaku sama.
Prasad Kanaparthi
Tidak, jelas tidak. Mereka tidak berperilaku sama, seperti yang ditunjukkan contoh pertama Anda.
Konrad Rudolph
Dalam contoh Anda, semua ID berbeda.
Jim Mischel
@JimMischel, Mengedit posting saya. Bahkan dengan nilai yang sama juga berperilaku sama.
Prasad Kanaparthi

Jawaban:

110

Union mengembalikan Distinctnilai. Secara default akan membandingkan referensi item. Item Anda memiliki referensi yang berbeda, sehingga semuanya dianggap berbeda. Saat Anda mentransmisikan ke tipe dasar X, referensi tidak berubah.

Jika Anda akan menimpa Equalsdan GetHashCode(digunakan untuk memilih item yang berbeda), maka item tidak akan dibandingkan dengan referensi:

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

Tetapi semua item Anda memiliki nilai yang berbeda ID. Jadi semua item masih dianggap berbeda. Jika Anda akan memberikan beberapa item dengan yang sama IDmaka Anda akan melihat perbedaan antara Uniondan 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

Sampel awal Anda berfungsi, karena bilangan bulat adalah jenis nilai dan dibandingkan berdasarkan nilai.

Sergey Berezovskiy
sumber
3
Bahkan jika itu tidak membandingkan referensi tetapi misalnya ID di dalamnya, akan tetap ada empat item karena ID berbeda.
Rawling
@Swani tidak, mereka tidak. Saya pikir Anda tidak mengubah ID item pertama dalam koleksi kedua, seperti yang saya nyatakan di atas
Sergey Berezovskiy
@Swani maka Anda belum menimpa Equals dan GetHashCode, seperti yang saya nyatakan di atas
Sergey Berezovskiy
@ lazyberezovsky, saya setuju dengan jawaban Anda. Tapi saya masih tidak senang dengan komentarnya. Jika Anda menjalankan kode sampel saya maka Anda dapat melihat hasil yang sama untuk 'a5' & 'a6'. Saya tidak mencari solusi. Tapi kenapa 'Concat' & 'Union' berperilaku sama pada situasi itu. Tolong dibalas.
Prasad Kanaparthi
3
@Swani maaf, afk. x.Union(y)sama dengan x.Concat(y).Distinct(). Jadi bedanya hanya dengan melamar Distinct. Bagaimana Linq memilih objek yang berbeda (yaitu yang berbeda) dalam urutan yang digabungkan? Dalam kode sampel Anda (dari pertanyaan) Linq membandingkan objek dengan referensi (yaitu alamat dalam memori). Ketika Anda membuat objek baru melalui newoperator, itu mengalokasikan memori di alamat baru. Jadi, ketika Anda memiliki empat objek yang baru dibuat, alamatnya akan berbeda. Dan semua objek akan berbeda. Dengan demikian Distinctakan mengembalikan semua objek dari urutan.
Sergey Berezovskiy
48

Concatsecara harfiah mengembalikan item dari urutan pertama diikuti dengan item dari urutan kedua. Jika Anda menggunakan Concatdua urutan 2 item, Anda akan selalu mendapatkan urutan 4 item.

Unionpada dasarnya Concatdiikuti oleh Distinct.

Dalam dua kasus pertama, Anda akan mendapatkan urutan 2 item karena, di antara keduanya, setiap pasangan kotak masukan memiliki tepat dua item yang berbeda.

Dalam kasus ketiga Anda, Anda berakhir dengan urutan 4 item karena keempat item dalam dua urutan masukan Anda berbeda .

Rawling
sumber
14

Uniondan Concatberperilaku sama karena Uniontidak dapat mendeteksi duplikat tanpa kebiasaan IEqualityComparer<X>. Hanya melihat jika keduanya adalah referensi yang sama.

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

Sekarang Anda dapat menggunakannya dalam kelebihan beban Union:

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