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
Якщо ви знаєте різницю між цими двома методами, чому результат вас дивує? Це прямий наслідок функціональності методів.
Конрад Рудольф

@KonradRudolph, Я маю на увазі те, що у випадку зі списком <T> я можу використовувати будь-який "Союз" / "Concat". Тому що обидва поводяться однаково.
Прасад Канапарті

Ні, очевидно, ні. Вони поводяться не так, як показує ваш перший приклад.
Конрад Рудольф

У вашому прикладі всі ідентифікатори різні.
Джим Мішель,

@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
Навіть якби це не було порівняння посилань, а, наприклад, ідентифікаторів всередині, все одно було б чотири елементи, оскільки ідентифікатори різні.
Роулінг

@Swani ні, вони не є. Я думаю, ви не змінили ідентифікатор першого предмета у другій колекції, як я вже говорив вище
Сергій Березовський

@Swani, тоді ви не перевизначили Equals та GetHashCode, як я вже говорив вище
Сергій Березовський

@lazyberezovsky, я згоден з вашою відповіддю. Але я все ще не задоволений коментарями. Якщо ви виконаєте мій зразок коду, то ви побачите той самий результат для 'a5' та 'a6'. Я не шукаю рішення. Але чому "Concat" та "Union" поводяться однаково при цій ситуації. Будь-ласка дайте відповідь.
Прасад Канапарті

3
@Swani вибачте, був afk. x.Union(y)це те саме, що x.Concat(y).Distinct(). Тож різниця полягає лише у поданні заявки Distinct. Як Linq відбирає різні (тобто різні) об'єкти в об'єднаних послідовностях? У зразку коду (з запитання) Linq порівнює об'єкти за посиланням (тобто адресою в пам'яті). Коли ви створюєте новий об'єкт за допомогою newоператора, він виділяє пам'ять за новою адресою. Отже, коли у вас є чотири нових створених об’єкта, адреси будуть різними. І всі предмети будуть різними. Таким чином Distinctповерне всі об'єкти з послідовності.
Сергій Березовський

48

Concatбуквально повертає елементи з першої послідовності, за якими слідують елементи з другої послідовності. Якщо ви використовуєте Concatдві послідовності з 2 елементів, ви завжди отримаєте послідовність із 4 елементів.

Unionпо суті Concatслідує Distinct.

У перших двох випадках ви отримуєте послідовності з 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()); 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.