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, я имею в виду, что в случае List <T> я могу использовать любой один 'Union' / 'Concat'. Потому что оба ведут себя одинаково.
— Прасад Канапарти

Нет, конечно, нет. Они не ведут себя так же, как показывает ваш первый пример.
— Конрад Рудольф

В вашем примере все идентификаторы разные.
— Джим Мишель

@JimMischel, отредактировал мой пост. даже с одинаковыми значениями он ведет себя одинаково.
— Прасад Канапарти

Ответы:


110

Union возвращает 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
Даже если сравнивать не ссылки, а, например, идентификаторы внутри, все равно будет четыре элемента, поскольку идентификаторы разные.
— Rawling

@Swani нет, это не так. Думаю, вы не меняли ID первого предмета во второй коллекции, как я уже говорил выше
— Сергей Березовский

@Swani, значит, вы не переопределяете Equals и GetHashCode, как я уже говорил выше
— Сергей Березовский,

@lazyberezovsky, согласен с вашим ответом. Но я все еще недоволен комментариями. Если вы выполните мой пример кода, вы увидите тот же результат для «a5» и «a6». Я не ищу решения. Но почему Concat и Union ведут себя одинаково в этой ситуации. Ответьте, пожалуйста.
— Прасад Канапарти

3
@Swani, извини, был афк x.Union(y)такое же, как x.Concat(y).Distinct(). Так что разница только в применении Distinct. Как Linq выбирает отдельные (т.е. разные) объекты в сцепленных последовательностях? В вашем примере кода (из вопроса) Linq сравнивает объекты по ссылке (то есть по адресу в памяти). Когда вы создаете новый объект с помощью newоператора, он выделяет память по новому адресу. Итак, когда у вас будет четыре новых созданных объекта, адреса будут другими. И все объекты будут отличными. Таким образом Distinctвернет все объекты из последовательности.
— Сергей Березовский

48

Concatбуквально возвращает элементы из первой последовательности, за которыми следуют элементы из второй последовательности. Если вы используете Concatдве последовательности из 2 элементов, вы всегда получите последовательность из 4 элементов.

Unionпо существу Concatследует Distinct.

В ваших первых двух случаях вы получите последовательности из двух элементов, потому что между ними каждая пара входных последовательностей имеет ровно два различных элемента.

В третьем случае вы получите последовательность из 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.