Union Vs Concat ใน Linq


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
หากคุณทราบความแตกต่างระหว่างสองวิธีนี้ทำไมผลลัพธ์ถึงทำให้คุณประหลาดใจ เป็นผลโดยตรงจากการทำงานของวิธีการ
Konrad Rudolph

@KonradRudolph, สิ่งที่ฉันหมายถึงคือกรณีของรายการ <T> ฉันสามารถใช้ 'Union' / 'Concat' ใดก็ได้ เพราะทั้งสองมีพฤติกรรมเหมือนกัน.
ปราสาทคณาปาตี

ไม่ไม่ชัดเจน พวกเขาไม่ทำงานเหมือนกันดังตัวอย่างแรกของคุณแสดงให้เห็น
Konrad Rudolph

ในตัวอย่างของคุณรหัสทั้งหมดจะแตกต่างกัน
Jim Mischel

@JimMischel แก้ไขโพสต์ของฉัน แม้จะมีค่าเดียวกัน แต่ก็มีพฤติกรรมเหมือนกัน
ปราสาทคณาปาตี

คำตอบ:


110

ยูเนี่ยนส่งคืน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 ต่างกัน
Rawling

@Swani ไม่พวกเขาไม่ได้ ฉันคิดว่าคุณไม่ได้เปลี่ยน ID ของรายการแรกในคอลเลกชั่นที่สองอย่างที่ฉันระบุไว้ข้างต้น
Sergey Berezovskiy

@Swani คุณยังไม่ได้แทนที่ Equals และ GetHashCode ตามที่ฉันระบุไว้ข้างต้น
Sergey Berezovskiy

@lazyberezovsky ฉันเห็นด้วยกับคำตอบของคุณ แต่ฉันยังไม่พอใจกับความคิดเห็น หากคุณรันโค้ดตัวอย่างของฉันคุณจะเห็นผลลัพธ์เดียวกันสำหรับ 'a5' & 'a6' ฉันไม่ได้มองหาวิธีแก้ปัญหา แต่ทำไม 'Concat' และ 'Union' ถึงมีพฤติกรรมเหมือนกันในสถานการณ์นั้น กรุณาตอบกลับ.
ปราสาทคณาปาตี

3
@Swani ขอโทษเป็น afk x.Union(y)เหมือนกับx.Concat(y).Distinct(). ความแตกต่างจึงเกิดขึ้นกับการสมัครDistinctเท่านั้น Linq เลือกอ็อบเจ็กต์ที่แตกต่างกัน (เช่นแตกต่างกัน) อย่างไรในลำดับต่อเนื่อง ในโค้ดตัวอย่างของคุณ (จากคำถาม) Linq เปรียบเทียบอ็อบเจ็กต์โดยการอ้างอิง (เช่นแอดเดรสในหน่วยความจำ) เมื่อคุณสร้างวัตถุใหม่ผ่านnewตัวดำเนินการจะจัดสรรหน่วยความจำตามที่อยู่ใหม่ ดังนั้นเมื่อคุณมีวัตถุที่สร้างขึ้นใหม่สี่ชิ้นที่อยู่จะแตกต่างกัน และวัตถุทั้งหมดจะแตกต่างกัน ดังนั้นDistinctจะส่งคืนวัตถุทั้งหมดจากลำดับ
Sergey Berezovskiy

48

Concatส่งคืนรายการจากลำดับแรกตามด้วยรายการจากลำดับที่สอง หากคุณใช้Concatกับลำดับ 2 รายการสองรายการคุณจะได้รับลำดับ 4 รายการเสมอ

Unionเป็นหลักตามด้วยConcatDistinct

ในสองกรณีแรกของคุณคุณจะได้ลำดับ 2 รายการเนื่องจากระหว่างนั้นกำลังสองของอินพุตแต่ละคู่มีสองรายการที่แตกต่างกัน

ในกรณีที่สามของคุณคุณจะจบลงด้วยลำดับที่ 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.