Union Vs Concat ở Linq


86

Tôi có một câu hỏi về UnionConcat. Tôi đoán cả hai đều hành xử giống nhau trong trường hợp 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"

Kết quả trên là mong đợi,

Nhưng List<T>tôi đang nhận được kết quả tương 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

Nhưng cả hai đều hành xử giống nhau List<T>.

Bất kỳ đề nghị xin vui lòng?


1
Nếu bạn biết sự khác biệt giữa hai phương pháp này, tại sao kết quả lại khiến bạn bất ngờ? Đó là hệ quả trực tiếp của chức năng của các phương pháp.
Konrad Rudolph

@KonradRudolph, Ý tôi là trong phạm vi Danh sách <T> tôi có thể sử dụng bất kỳ một 'Union' / 'Concat' nào. Bởi vì cả hai đều đang cư xử giống nhau.
Prasad Kanaparthi

Không, rõ ràng là không. Chúng không hoạt động giống nhau, như ví dụ đầu tiên của bạn cho thấy.
Konrad Rudolph

Trong ví dụ của bạn, tất cả các ID đều khác nhau.
Jim Mischel,

@JimMischel, Đã chỉnh sửa bài đăng của tôi. ngay cả với các giá trị giống nhau nó cũng hoạt động như nhau.
Prasad Kanaparthi

Câu trả lời:


110

Union trả về Distinctgiá trị. Theo mặc định, nó sẽ so sánh các tham chiếu của các mục. Các mục của bạn có các tham chiếu khác nhau, do đó tất cả chúng được coi là khác nhau. Khi bạn truyền sang kiểu cơ sở X, tham chiếu không bị thay đổi.

Nếu bạn sẽ ghi đè EqualsGetHashCode(được sử dụng để chọn các mục riêng biệt), thì các mục sẽ không được so sánh bằng tham chiếu:

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

Nhưng tất cả các mặt hàng của bạn có giá trị khác nhau ID. Vì vậy, tất cả các mục vẫn được coi là khác nhau. Nếu bạn cung cấp một số mục giống nhau IDthì bạn sẽ thấy sự khác biệt giữa UnionConcat:

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

Mẫu ban đầu của bạn hoạt động vì số nguyên là kiểu giá trị và chúng được so sánh theo giá trị.


3
Ngay cả khi nó không so sánh các tham chiếu nhưng ví dụ như các ID bên trong, vẫn sẽ có bốn mục vì các ID khác nhau.
Rawling

@Swani nope, họ không phải vậy. Tôi nghĩ rằng bạn đã không thay đổi ID của mặt hàng đầu tiên trong bộ sưu tập thứ hai, như tôi đã nói ở trên
Sergey Berezovskiy

@Swani thì bạn chưa ghi đè Equals và GetHashCode, như tôi đã nêu ở trên
Sergey Berezovskiy

@lazyberezovsky, tôi đồng ý với câu trả lời của bạn. Nhưng tôi vẫn không hài lòng với các ý kiến. Nếu bạn thực thi mã mẫu của tôi thì bạn có thể thấy kết quả tương tự cho 'a5' & 'a6'. Tôi không tìm kiếm giải pháp. Nhưng tại sao 'Concat' & 'Union' lại hoạt động giống nhau ở sự phản kháng đó. Xin hãy trả lời.
Prasad Kanaparthi

3
@Swani xin lỗi, là afk. x.Union(y)giống như x.Concat(y).Distinct(). Vì vậy, sự khác biệt chỉ là khi áp dụng Distinct. Làm thế nào Linq chọn các đối tượng riêng biệt (tức là khác nhau) trong các chuỗi được nối? Trong mã mẫu của bạn (từ câu hỏi) Linq so sánh các đối tượng bằng tham chiếu (tức là địa chỉ trong bộ nhớ). Khi bạn tạo đối tượng mới thông qua newtoán tử, nó sẽ cấp phát bộ nhớ tại địa chỉ mới. Vì vậy, khi bạn có bốn đối tượng mới được tạo, các địa chỉ sẽ khác nhau. Và tất cả các đối tượng sẽ khác biệt. Do đó Distinctsẽ trả về tất cả các đối tượng từ chuỗi.
Sergey Berezovskiy

48

Concattheo nghĩa đen trả về các mục từ chuỗi đầu tiên, theo sau là các mục từ chuỗi thứ hai. Nếu bạn dùngConcat trên hai chuỗi 2 mục, bạn sẽ luôn nhận được chuỗi 4 mục.

Unionvề cơ bản được Concattheo sau bởi Distinct.

Trong hai trường hợp đầu tiên của bạn, bạn kết thúc với chuỗi 2 mục vì giữa chúng, mỗi cặp bình phương đầu vào có chính xác hai mục riêng biệt.

Trong trường hợp thứ ba của bạn, bạn kết thúc với một chuỗi 4 mục vì tất cả bốn mục trong hai chuỗi đầu vào của bạn là khác biệt .


14

UnionConcathoạt động giống nhau vì Unionkhông thể phát hiện các bản sao nếu không có tùy chỉnh IEqualityComparer<X>. Nó chỉ đang xem xét nếu cả hai cùng một tham chiếu.

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

Bây giờ bạn có thể sử dụng nó khi quá tải Union:

var comparer = new XComparer();
a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>(), new XComparer()); 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.