Łączenie dwóch list razem


333

Jeśli mam dwie listy ciągów znaków (lub dowolnego innego typu), jaki jest szybki sposób na połączenie tych dwóch list?

Zamówienie powinno pozostać takie samo. Duplikaty należy usunąć (choć każdy element w obu linkach jest unikalny). Nie znalazłem wiele na ten temat, kiedy googlowałem i nie chciałem implementować żadnych interfejsów .NET dla szybkości dostarczania.


5
Czy zamówienie ma znaczenie? Czy chcesz zachować duplikaty?
Larsenal

Odpowiedzi:


573

Możesz spróbować:

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

a.AddRange(b);

Strona MSDN dla AddRange

Zachowuje to kolejność list, ale nie usuwa duplikatów, które Unionby to zrobiły.

To zmienia listę a. Jeśli chcesz zachować oryginalne listy, powinieneś użyć Concat(jak wskazano w innych odpowiedziach):

var newList = a.Concat(b);

Zwraca IEnumerabletyle, ile anie jest zerowe.


27
Nikt tak naprawdę nie zastanawiał się, kiedy użyć tej metody. AddRange edytuje listę w miejscu, dodając do niej drugą listę (tak jakbyś wywoływał .Add (foo) kilka razy). Metody rozszerzenia Concat i Union nie zmieniają oryginalnej listy. Leniwie konstruują nowy IEnumerable i nie będą nawet uzyskiwać dostępu do oryginalnych członków listy, chyba że będzie to konieczne. Jak wspomniano, Unia usuwa duplikaty, a inne nie.
ShawnFumo

4
Czy ktoś wie, jaka jest złożoność tej metody? (Szkoda, że ​​Microsoft nie podaje tych ważnych informacji w ramach MSDN)
Jacob

2
Polecam sprawdzić to dobre porównanie różnych podejść. Pokaż także analizę wydajności różnych opcji: Scal kolekcje
BogeyMan

To działa idealnie. Używał concat () na ponad pół miliona elementów, co zajęło kilka minut. Takie podejście zajmuje mniej niż 5 sekund.
GreenFerret95

111

Sposobem z najmniejszym narzutem jest użycie metody rozszerzenia Concat.

var combined = list1.Concat(list2);

Tworzy instancję, IEnumerable<T>która wyliczy elementy list1 i list2 w tej kolejności.


2
pamiętaj, aby using System.Linq;móc używać Concat
tothemario

43

Metoda unijna może zaspokoić Twoje potrzeby. Nie określono, czy zamówienie lub duplikaty są ważne.

Weź dwa IEnumerables i wykonaj połączenie, jak pokazano tutaj:

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

Coś takiego:

firstList.AddRange (secondList);

Możesz także użyć metody rozszerzenia „Union” zdefiniowanej w System.Linq. Za pomocą „Unii” można również określić moduł porównujący, którego można użyć do określenia, czy element powinien zostać zjednoczony, czy nie.

Lubię to:

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

Myślę, że działa dobrze. Jak wcześniej wspomniano, Concat zwraca nową sekwencję i podczas konwersji wyniku na Listę wykonuje to zadanie doskonale. Niejawne konwersje mogą czasami zawieść przy użyciu metody AddRange.


13

Jeśli jakieś elementy istnieją na obu listach, możesz użyć

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

7

O ile są one tego samego typu, AddRange jest bardzo proste:

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 }

Też to lubię. Bardzo prosta. Dzięki.
GurdeepS,

Podoba mi się to, ponieważ nie mutuje żadnej z list.
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

Jeden ze sposobów, o którym nie wspomniałem, może być nieco bardziej niezawodny, szczególnie jeśli chciałbyś w jakiś sposób zmienić każdy element (np. Chciałeś .Trim()wszystkich elementów.

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

1

Zobacz ten 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 } };

// Pobierz produkty z obu tablic // z wyłączeniem duplikatów.

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

Dwie opcje, których używam to:

list1.AddRange(list2);

lub

list1.Concat(list2);

Zauważyłem jednak, że korzystałem z niego podczas korzystania z AddRange metody z funkcją rekurencyjną, która wywołuje się bardzo często, otrzymałem wyjątek SystemOutOfMemoryException, ponieważ osiągnięto maksymalną liczbę wymiarów.

(Wiadomość przetłumaczona z Google)
Wymiary tablicy przekroczyły obsługiwany zakres.

Korzystanie Concatrozwiązało ten problem.


0

Chciałem tylko przetestować jak Union działa domyślny moduł porównujący w nakładających się kolekcjach obiektów typu referencyjnego.

Moim celem jest:

class MyInt
{
    public int val;

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

Mój kod testowy to:

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

Dane wyjściowe to:

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

Więc wszystko działa dobrze.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.