Unindo duas listas


333

Se eu tiver duas listas do tipo string (ou qualquer outro tipo), qual é a maneira mais rápida de ingressar nas duas listas?

O pedido deve permanecer o mesmo. As duplicatas devem ser removidas (embora todos os itens nos dois links sejam únicos). Não encontrei muito sobre isso ao pesquisar no Google e não queria implementar nenhuma interface .NET para agilizar a entrega.


5
A ordem importa? Deseja reter duplicados?
Larsenal 06/10/2009

Respostas:


573

Você poderia tentar:

List<string> a = new List<string>();
List<string> b = new List<string>();

a.AddRange(b);

Página MSDN para AddRange

Isso preserva a ordem das listas, mas não remove as duplicatas que Unionseriam necessárias.

Isso muda a lista a. Se você deseja preservar as listas originais, use Concat(como indicado nas outras respostas):

var newList = a.Concat(b);

Isso retorna um IEnumerabledesde que anão seja nulo.


27
Ninguém realmente se interessou quando usar qual método. O AddRange edita uma lista no local, adicionando a segunda lista (como se você chamasse .Add (foo) várias vezes). Os métodos de extensão Concat e Union não alteram a lista original. Eles constroem preguiçosamente um novo IEnumerable e nem acessam os membros da lista original, a menos que seja necessário. Como observado, o Union remove duplicatas, enquanto os outros não.
ShawnFumo

4
Alguém sabe qual é a complexidade desse método? (É por isso vergonha Microsoft não fornecer esta informação importante como parte de sua MSDN)
Jacob

2
Eu recomendo que você verifique essa boa comparação das várias abordagens. Mostre também algumas análises de desempenho de várias opções: Mesclar coleções
BogeyMan

Isso funciona perfeitamente. Estava usando concat () em mais de meio milhão de itens, o que levou vários minutos. Essa abordagem leva menos de 5 segundos.
GreenFerret95

110

A maneira com o mínimo de sobrecarga de espaço é usar o método de extensão Concat.

var combined = list1.Concat(list2);

Ele cria uma instância da IEnumerable<T>qual enumerará os elementos de list1 e list2 nessa ordem.


2
lembre- using System.Linq;se de poder usar o Concat
tothemario 5/09/19

43

O método Union pode atender às suas necessidades. Você não especificou se o pedido ou as duplicatas eram importantes.

Pegue dois IEnumerables e faça uma união como visto aqui:

int[] ints1 = { 5, 3, 9, 7, 5, 9, 3, 7 };
int[] ints2 = { 8, 3, 6, 4, 4, 9, 1, 0 };

IEnumerable<int> union = ints1.Union(ints2);

// yields { 5, 3, 9, 7, 8, 6, 4, 1, 0 } 

24

Algo assim:

firstList.AddRange (secondList);

Ou você pode usar o método de extensão 'Union' definido em System.Linq. Com 'União', você também pode especificar um comparador, que pode ser usado para especificar se um item deve ser sindicalizado ou não.

Como isso:

List<int> one = new List<int> { 1, 2, 3, 4, 5 };
List<int> second=new List<int> { 1, 2, 5, 6 };

var result = one.Union (second, new EqComparer ());

foreach( int x in result )
{
    Console.WriteLine (x);
}
Console.ReadLine ();

#region IEqualityComparer<int> Members
public class EqComparer : IEqualityComparer<int>
{
    public bool Equals( int x, int y )
    {
        return x == y;
    }

    public int GetHashCode( int obj )
    {
        return obj.GetHashCode ();
    }
}
#endregion

17
targetList = list1.Concat(list2).ToList();

Está funcionando bem, acho que sim. Como dito anteriormente, o Concat retorna uma nova sequência e, ao converter o resultado em Lista, ele executa o trabalho perfeitamente. Às vezes, conversões implícitas podem falhar ao usar o método AddRange.


13

Se alguns itens existirem nas duas listas, você poderá usar

var all = list1.Concat(list2).Concat(list3) ... Concat(listN).Distinct().ToList();

7

Desde que sejam do mesmo tipo, é muito simples com o AddRange:

list2.AddRange(list1);

7
var bigList = new List<int> { 1, 2, 3 }
    .Concat(new List<int> { 4, 5, 6 })
    .ToList(); /// yields { 1, 2, 3, 4, 5, 6 }

Eu também gosto disso. Muito simples. Obrigado.
GurdeepS 6/10/09

Eu gosto disso porque não muda nenhuma das listas.
Ev.


4
List<string> list1 = new List<string>();
list1.Add("dot");
list1.Add("net");

List<string> list2 = new List<string>();
list2.Add("pearls");
list2.Add("!");

var result = list1.Concat(list2);


2

De uma maneira, eu não vi mencionado, que pode ser um pouco mais robusto, principalmente se você quiser alterar cada elemento de alguma forma (por exemplo, você deseja .Trim()todos os elementos.

List<string> a = new List<string>();
List<string> b = new List<string>();
// ...
b.ForEach(x=>a.Add(x.Trim()));

1

Veja este link

public class ProductA
{ 
public string Name { get; set; }
public int Code { get; set; }
}

public class ProductComparer : IEqualityComparer<ProductA>
{

public bool Equals(ProductA x, ProductA y)
{
    //Check whether the objects are the same object. 
    if (Object.ReferenceEquals(x, y)) return true;

    //Check whether the products' properties are equal. 
    return x != null && y != null && x.Code.Equals(y.Code) && x.Name.Equals(y.Name);
    }

public int GetHashCode(ProductA obj)
{
    //Get hash code for the Name field if it is not null. 
    int hashProductName = obj.Name == null ? 0 : obj.Name.GetHashCode();

    //Get hash code for the Code field. 
    int hashProductCode = obj.Code.GetHashCode();

    //Calculate the hash code for the product. 
    return hashProductName ^ hashProductCode;
}
}


    ProductA[] store1 = { new ProductA { Name = "apple", Code = 9 }, 
                   new ProductA { Name = "orange", Code = 4 } };

    ProductA[] store2 = { new ProductA { Name = "apple", Code = 9 }, 
                   new ProductA { Name = "lemon", Code = 12 } };

// Obtenha os produtos das duas matrizes // excluindo duplicatas.

IEnumerable<ProductA> union =
  store1.Union(store2);

foreach (var product in union)
    Console.WriteLine(product.Name + " " + product.Code);

/*
    This code produces the following output:

    apple 9
    orange 4
    lemon 12
*/

1

As duas opções que eu uso são:

list1.AddRange(list2);

ou

list1.Concat(list2);

No entanto, notei que ao usar o AddRangemétodo com uma função recursiva, que se chama com muita frequência, recebi uma SystemOutOfMemoryException porque o número máximo de dimensões foi atingido.

(Mensagem traduzida pelo Google)
As dimensões da matriz excederam o intervalo suportado.

Usando Concatresolveu esse problema.


0

Eu só queria testar como Unionfunciona com o comparador padrão em coleções sobrepostas de objetos do tipo referência.

Meu objeto é:

class MyInt
{
    public int val;

    public override string ToString()
    {
        return val.ToString();
    }
}

Meu código de teste é:

MyInt[] myInts1 = new MyInt[10];
MyInt[] myInts2 = new MyInt[10];
int overlapFrom = 4;
Console.WriteLine("overlapFrom: {0}", overlapFrom);

Action<IEnumerable<MyInt>, string> printMyInts = (myInts, myIntsName) => Console.WriteLine("{2} ({0}): {1}", myInts.Count(), string.Join(" ", myInts), myIntsName);

for (int i = 0; i < myInts1.Length; i++)
    myInts1[i] = new MyInt { val = i };
printMyInts(myInts1, nameof(myInts1));

int j = 0;
for (; j + overlapFrom < myInts1.Length; j++)
    myInts2[j] = myInts1[j + overlapFrom];
for (; j < myInts2.Length; j++)
    myInts2[j] = new MyInt { val = j + overlapFrom };
printMyInts(myInts2, nameof(myInts2));

IEnumerable<MyInt> myUnion = myInts1.Union(myInts2);
printMyInts(myUnion, nameof(myUnion));

for (int i = 0; i < myInts2.Length; i++)
    myInts2[i].val += 10;
printMyInts(myInts2, nameof(myInts2));
printMyInts(myUnion, nameof(myUnion));

for (int i = 0; i < myInts1.Length; i++)
    myInts1[i].val = i;
printMyInts(myInts1, nameof(myInts1));
printMyInts(myUnion, nameof(myUnion));

A saída é:

overlapFrom: 4
myInts1 (10): 0 1 2 3 4 5 6 7 8 9
myInts2 (10): 4 5 6 7 8 9 10 11 12 13
myUnion (14): 0 1 2 3 4 5 6 7 8 9 10 11 12 13
myInts2 (10): 14 15 16 17 18 19 20 21 22 23
myUnion (14): 0 1 2 3 14 15 16 17 18 19 20 21 22 23
myInts1 (10): 0 1 2 3 4 5 6 7 8 9
myUnion (14): 0 1 2 3 4 5 6 7 8 9 20 21 22 23

Então, tudo funciona bem.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.