Об’єднайте два списки разом


333

Якщо у мене є два списки рядка типу (або будь-якого іншого типу), який швидкий спосіб з'єднання цих двох списків?

Порядок повинен залишатися колишнім. Дублікати потрібно видалити (хоча кожен елемент в обох посиланнях унікальний). Я не знайшов багато з цього питання під час googling і не хотів реалізовувати будь-які .NET інтерфейси для швидкості доставки.


5
Чи має значення замовлення? Ви хочете зберегти дублікати?
Ларсенал

Відповіді:


573

Ви можете спробувати:

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

a.AddRange(b);

Сторінка MSDN для AddRange

Це зберігає порядок списків, але він не видаляє жодних дублікатів, які Unionмогли б зробити.

Це робить список змін a. Якщо ви хочете зберегти оригінальні списки, тоді вам слід скористатися Concat(як зазначено в інших відповідях):

var newList = a.Concat(b);

Це повертається до IEnumerableтих пір, aпоки не буде нульовим.


27
Ніхто не задумався коли використовувати якийсь метод. AddRange редагує список на місці, додаючи до нього другий список (як би ви назвали .Add (foo) купу разів). Методи розширення Concat та Union не змінюють початковий список. Вони ліниво створюють новий IEnumerable і навіть не потребують доступу до первинних членів списку, якщо це не потрібно. Як зазначалося, Union видаляє дублікати, а інші -.
ShawnFumo

4
Хтось знає, у чому полягає складність цього методу? (Це соромно Microsoft не надає цю важливу інформацію як частину своєї MSDN)
Яків

2
Я рекомендую вам перевірити це хороше порівняння різних підходів. Також покажіть аналіз ефективності різних варіантів: Злиття колекцій
BogeyMan

Це прекрасно працює. Використовував concat () для більш ніж півмільйона предметів, що займало кілька хвилин. Такий підхід займає менше 5 секунд.
GreenFerret95

111

Шлях із найменшими накладними витратами - це використання методу розширення Concat.

var combined = list1.Concat(list2);

Він створює екземпляр, IEnumerable<T>який буде перераховувати елементи list1 та list2 у цьому порядку.


2
пам'ятайте, щоб using System.Linq;мати можливість використовувати Concat
tothemario

43

Метод Union може відповідати вашим потребам. Ви не вказали, чи важливими є порядок чи копії.

Візьміть два IEnumerables і виконайте об'єднання, як показано тут:

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

Щось на зразок цього:

firstList.AddRange (secondList);

Або ви можете використовувати метод розширення 'Union', визначений у System.Linq. За допомогою 'Union' ви також можете вказати компаратор, який можна використовувати для визначення того, чи слід об'єднати елемент чи ні.

Подобається це:

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

Я думаю, що так. Як було сказано раніше, Concat повертає нову послідовність і, перетворюючи результат у List, виконує цю роботу ідеально. Неявні перетворення іноді можуть не працювати при використанні методу AddRange.


13

Якщо деякі елементи є в обох списках, які ви можете використовувати

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


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

Мені це теж подобається. Дуже просто. Дякую.
GurdeepS

Мені це подобається, тому що він не мутує жодного списку.
Єв.


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

Один із способів я не бачив згадки про те, що може бути трохи надійнішим, особливо, якщо ви хотіли змінити кожен елемент якимось чином (наприклад, ви хотіли .Trim()всіх елементів.

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

1

Дивіться це посилання

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

// Отримати продукти з обох масивів // виключаючи дублікати.

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

Я використовую два варіанти:

list1.AddRange(list2);

або

list1.Concat(list2);

Однак я помітив, як використовував AddRangeметод, що використовує рекурсивну функцію, яка дуже часто викликає себе, я отримував SystemOutOfMemoryException, оскільки було досягнуто максимальну кількість розмірів.

(Повідомлення Google Translated)
Розміри масиву перевищили підтримуваний діапазон.

Використовуючи Concatвирішене питання.


0

Я просто хотів перевірити, як Unionпрацює порівняльник за замовчуванням на збірних колекціях об'єктів еталонного типу.

Мій об’єкт:

class MyInt
{
    public int val;

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

Мій тестовий код:

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

Вихід:

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

Отже, все працює добре.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.