2009-10-06 31 views
220

Jeśli mam dwie listy typu ciąg (lub dowolny inny typ), jaki jest szybki sposób dołączenia do dwóch list?Łączenie dwóch list razem

Zamówienie powinno pozostać niezmienione. Duplikaty powinny zostać usunięte (chociaż każdy element w obu linkach jest unikalny). Podczas wyszukiwania w Google nie znalazłem zbyt wiele informacji i nie chciałem implementować żadnych interfejsów .NET pod kątem szybkości dostarczania.

+4

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

+1

Możliwy duplikat * [Jak łączysz listy w języku C#?] (Http://stackoverflow.com/questions/1042219/how-do-you-concatenate-lists-in-c) *. –

Odpowiedz

380

Można spróbować:

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

a.AddRange(b); 

MSDN page for AddRange

ten zachowuje kolejność na listach, ale nie usunąć duplikaty, które Union zrobi .

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

var newList = a.Concat(b); 

ta zwraca IEnumerable dopóki a nie jest null.

+13

Nikt tak naprawdę nie zaglądał, kiedy użyć tej metody. AddRange edytuje listę w miejscu, dodając do niej drugą listę (tak jakbyś kilka razy dzwonił .Dodaj (foo)). Metody Concat i Union nie zmieniają pierwotnej listy. Oni leniwie budują nowy IEnumerable i nie będą nawet mieli dostępu do oryginalnych członków list, chyba że będzie to konieczne. Jak wspomniano, Unia usuwa duplikaty, podczas gdy inne nie. – ShawnFumo

+2

'concat' nie działa, gdy jedna z list ma wartość null – Amit

+3

Czy ktoś wie, jaka jest złożoność tej metody? (To wstyd, że Microsoft nie dostarcza tych ważnych informacji w ramach MSDN). – Jacob

19

coś takiego:

firstList.AddRange (secondList); 

Lub można użyć metody rozszerzenie „Unii”, który jest zdefiniowany w System.Linq. Za pomocą "Union" można również określić porównywarkę, która może być użyta do określenia, czy element powinien być połączony, czy nie.

Jak 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 
34

Metoda Union może zaspokoić Twoje potrzeby. Nie określono, czy zamówienie lub duplikaty były ważne.

Weź dwa IEnumerables i wykonać unii jak widać 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 } 
3

jeden sposób: List.AddRange() w zależności od rodzaju?

85

Droga z najmniejszą przestrzeń nad głową jest użycie metody rozszerzenie Concat.

var combined = list1.Concat(list2); 

tworzy egzemplarz IEnumerable<T> który wyszczególnia elementy listy1 i listy2 w tej kolejności.

7

Dopóki są one tego samego typu, to bardzo proste z AddRange:

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

Podoba mi się to. Bardzo prosta. Dzięki. – dotnetdev

+0

Podoba mi się to, ponieważ nie mutuje żadnej z tych list. –

10

Jeśli jakiś przedmiot (y) występują na obu listach możesz użyć

var all = list1.Concat(list2).Concat(list3) ... Concat(listN).Distinct().ToList(); 
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); 
12
targetList = list1.Concat(list2).ToList(); 

Działa to dobrze, tak myślę. Jak wcześniej wspomniano, Concat zwraca nową sekwencję, a konwertując wynik na Listę, wykonuje to zadanie doskonale. Konwersje niejawne mogą czasem zawieść, gdy używasz metody AddRange.

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 // 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 
*/ 
0

Chciałem tylko sprawdzić, jak Union działa z domyślnym comparer na nakładających zbiory obiektów typu odniesienia.

Moim celem jest:

class MyInt 
{ 
    public int val; 

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

Mój kod test jest:

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

Wyjście jest:

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 

Tak, wszystko działa bez zarzutu.