İki listeyi birleştirme


333

İki tür dize listesi (veya başka bir tür) varsa, iki listeye katılmanın hızlı bir yolu nedir?

Sipariş aynı kalmalıdır. Kopyalar kaldırılmalıdır (her iki bağlantıdaki her öğe benzersiz olsa da). Google'da bu konuda fazla bir şey bulamadım ve teslimat hızı için herhangi bir .NET arayüzü uygulamak istemedim.


5
Sipariş önemli mi? Kopyaları korumak istiyor musunuz?
Larsenal

Yanıtlar:


573

Deneyebilirsiniz:

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

a.AddRange(b);

İçin MSDN sayfası AddRange

Bu, listelerin sırasını korur, ancak Unionyapacak kopyaları kaldırmaz .

Bu listeyi değiştirir a. Orijinal listeleri korumak istiyorsanız Concat(diğer cevaplarda belirtildiği gibi) kullanmalısınız:

var newList = a.Concat(b);

Bu , null olmadığı IEnumerablesürece bir döndürür a.


27
Kimse hangi yöntemi ne zaman kullanacağını bilmiyor. AddRange bir listeyi yerinde düzenler, ikinci listeyi listeye ekler (sanki .Add (foo) adını bir kaç kez çağırdınız). Concat ve Union genişletme yöntemleri orijinal listeyi değiştirmez. Tembel olarak yeni bir IEnumerable oluştururlar ve gerekmedikçe orijinal liste üyelerine bile erişmezler. Daha önce de belirtildiği gibi, Birlik yinelemeleri kaldırırken diğerleri yapmaz.
ShawnFumo

4
Bu yöntemin karmaşıklığının ne olduğunu bilen var mı? (Microsoft'un bu önemli bilgileri
Jacob

2
Çeşitli yaklaşımların bu iyi karşılaştırmasını kontrol etmenizi tavsiye ederim. Ayrıca çeşitli seçeneklerin bazı performans analizlerini göster: Koleksiyonları Birleştir
BogeyMan

Bu mükemmel çalışıyor. Birkaç dakika süren yarım milyondan fazla ürün üzerinde concat () kullanıyordu. Bu yaklaşım 5 saniyeden az sürer.
GreenFerret95

111

En az alan yükü olan yol Concat uzatma yöntemini kullanmaktır.

var combined = list1.Concat(list2);

IEnumerable<T>List1 ve list2 öğelerini bu sırayla numaralandıracak bir örneği oluşturur .


2
unutmayın using System.Linq;CONCAT kullanmak mümkün
tothemario

43

Birlik yöntem ihtiyaçlarına cevap olabilir. Siparişin mi yoksa kopyaların önemli mi olduğunu belirtmediniz.

İki IEnumerable alın ve burada görüldüğü gibi bir birlik gerçekleştirin:

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

Bunun gibi bir şey:

firstList.AddRange (secondList);

Veya System.Linq dosyasında tanımlanan 'Union' uzantı yöntemini kullanabilirsiniz. 'Birlik' ile, bir öğenin birleştirilip birleştirilmemesi gerektiğini belirtmek için kullanılabilecek bir karşılaştırıcı da belirleyebilirsiniz.

Bunun gibi:

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

Bence iyi çalışıyor. Daha önce de belirtildiği gibi, Concat yeni bir dizi döndürür ve sonucu List'e dönüştürürken işi mükemmel bir şekilde yapar. Örtülü dönüştürmeler bazen AddRange yöntemi kullanılırken başarısız olabilir.


13

Her iki listede de bazı öğeler mevcutsa,

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

7

Aynı türde oldukları sürece AddRange ile çok basittir:

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 }

Bundan bende hoşlanıyorum. Çok basit. Teşekkürler.
GurdeepS

Bunu beğendim çünkü her iki listeyi de değiştirmiyor.
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

Bir şekilde, özellikle her bir öğeyi bir şekilde değiştirmek istiyorsanız (örneğin, .Trim()tüm öğelere istediğinizde) biraz daha sağlam olabileceğinden bahsetmedim .

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

1

Bu bağlantıya bakın

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

// Her iki diziden de ürünleri al // kopyalar hariç.

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

Kullandığım iki seçenek:

list1.AddRange(list2);

veya

list1.Concat(list2);

Ancak ben AddRangeözyinelemeli bir işlevle yöntemi kullanırken , bu kendini sık sık çağıran kullandığım gibi ben maksimum boyut sayısına ulaşıldı çünkü SystemOutOfMemoryException aldım fark ettim .

(Google Çeviri'ye İletildi )
Dizi boyutları desteklenen aralığı aşıyor.

Kullanmak Concatbu sorunu çözdü.


0

Ben sadece Unionüst üste gelen referans türü nesnelerin varsayılan karşılaştırıcı ile nasıl çalıştığını test etmek istedim .

Hedefim:

class MyInt
{
    public int val;

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

Test kodum:

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

Çıktı:

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

Yani, her şey yolunda gidiyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.