Соединение двух списков вместе


333

Если у меня есть два списка типа string (или любого другого типа), как быстро соединить два списка?

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


5
Имеет ли значение заказ? Хотите сохранить дубликаты?
Ларсенал

Ответы:


574

Вы можете попробовать:

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

Пока они одного типа, с 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 }

Мне это тоже нравится Очень просто. Спасибо.
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.