Union Vs Concat en Linq


86

Tengo una pregunta sobre Uniony Concat. Supongo que ambos se comportan igual en el caso de 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"

Se espera el resultado anterior,

Pero en caso de que obtengo el List<T>mismo resultado.

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

Pero ambos se comportan igual en el caso de List<T>.

¿Alguna sugerencia por favor?


1
Si conoce la diferencia entre estos dos métodos, ¿por qué le sorprende el resultado? Es una consecuencia directa de la funcionalidad de los métodos.
Konrad Rudolph

@KonradRudolph, lo que quiero decir es que, en caso de List <T>, puedo usar cualquier 'Unión' / 'Concat'. Porque ambos se comportan igual.
Prasad Kanaparthi

No, obviamente no. No se comportan igual, como muestra su primer ejemplo.
Konrad Rudolph

En su ejemplo, todos los ID son diferentes.
Jim Mischel

@JimMischel, Editó mi publicación. incluso con los mismos valores también se comporta igual.
Prasad Kanaparthi

Respuestas:


110

Union devuelve Distinctvalores. De forma predeterminada, comparará referencias de artículos. Sus artículos tienen diferentes referencias, por lo que todos se consideran diferentes. Cuando lanza a tipo base X, la referencia no cambia.

Si anula Equalsy GetHashCode(utilizado para seleccionar elementos distintos), los elementos no se compararán por referencia:

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

Pero todos sus artículos tienen un valor diferente de ID. Así que todos los elementos se siguen considerando diferentes. Si proporciona varios artículos con el mismo ID, verá la diferencia entre Uniony 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

Su muestra inicial funciona, porque los números enteros son tipos de valor y se comparan por valor.


3
Incluso si no se comparan referencias, por ejemplo, los ID dentro, todavía habría cuatro elementos ya que los ID son diferentes.
Rawling

@Swani no, no lo son. Creo que no cambió la identificación del primer artículo en la segunda colección, como dije anteriormente
Sergey Berezovskiy

@Swani, entonces no ha anulado Equals y GetHashCode, como dije anteriormente
Sergey Berezovskiy

@lazyberezovsky, estoy de acuerdo con tu respuesta. Pero todavía no estoy contento con los comentarios. Si ejecuta mi código de muestra, puede ver el mismo resultado para 'a5' y 'a6'. No busco una solución. Pero por qué 'Concat' y 'Union' se comportan igual en esa situación. Por favor responde.
Prasad Kanaparthi

3
@Swani lo siento, estaba afk. x.Union(y)es lo mismo que x.Concat(y).Distinct(). Entonces la diferencia es solo con la aplicación Distinct. ¿Cómo selecciona Linq objetos distintos (es decir, diferentes) en secuencias concatenadas? En su código de muestra (de la pregunta) Linq compara objetos por referencia (es decir, dirección en la memoria). Cuando crea un nuevo objeto a través del newoperador, asigna memoria en la nueva dirección. Entonces, cuando tenga cuatro nuevos objetos creados, las direcciones serán diferentes. Y todos los objetos serán distintos. Por Distinctlo tanto , devolverá todos los objetos de la secuencia.
Sergey Berezovskiy

48

Concatliteralmente devuelve los elementos de la primera secuencia seguidos de los elementos de la segunda secuencia. Si utiliza Concaten dos secuencias de 2 elementos, siempre obtendrá una secuencia de 4 elementos.

Uniones Concatseguido esencialmente por Distinct.

En sus dos primeros casos, termina con secuencias de 2 elementos porque, entre ellos, cada par de secuencias de entrada tiene exactamente dos elementos distintos.

En su tercer caso, termina con una secuencia de 4 elementos porque los cuatro elementos en sus dos secuencias de entrada son distintos .


14

Uniony se Concatcomportan igual ya Unionque no se pueden detectar duplicados sin una costumbre IEqualityComparer<X>. Solo busca si ambos son la misma referencia.

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

Ahora puedes usarlo en la sobrecarga de Union:

var comparer = new XComparer();
a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>(), new XComparer()); 
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.