เข้าร่วมสองรายการด้วยกัน


333

หากฉันมีสองประเภทรายการของสตริง (หรือประเภทอื่น ๆ ) วิธีที่รวดเร็วในการเข้าร่วมทั้งสองรายการคืออะไร?

คำสั่งควรจะเหมือนเดิม รายการซ้ำควรถูกลบ (แม้ว่าทุกรายการในลิงก์ทั้งสองจะไม่ซ้ำกัน) ฉันไม่พบสิ่งนี้มากนักเมื่อ googling และไม่ต้องการใช้อินเตอร์เฟส. NET ใด ๆ เพื่อความเร็วในการส่ง


5
การสั่งซื้อมีความสำคัญหรือไม่ คุณต้องการเก็บข้อมูลที่ซ้ำกันหรือไม่
Larsenal

คำตอบ:


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 แก้ไขรายการในสถานที่เพิ่มรายการที่สองในนั้น (ราวกับว่าคุณเรียกว่า. เพิ่ม (foo) หลายครั้ง) เมธอดส่วนขยาย Concat และ Union ไม่เปลี่ยนรายการดั้งเดิม พวกเขาเกียจคร้านสร้าง IEnumerable ใหม่และจะไม่แม้แต่เข้าถึงรายชื่อสมาชิกดั้งเดิมยกเว้นว่าจำเป็น ตามที่ระบุไว้ Union จะลบรายการที่ซ้ำกันในขณะที่รายการอื่นไม่แสดง
ShawnFumo

4
ไม่มีใครรู้ว่าความซับซ้อนของวิธีนี้คืออะไร? (เป็นเรื่องที่น่าละอายมากที่ Microsoft ไม่ได้ให้ข้อมูลสำคัญนี้ในฐานะเป็นส่วนหนึ่งของ MSDN)
Jacob

2
ฉันขอแนะนำให้คุณตรวจสอบการเปรียบเทียบที่ดีของวิธีการต่าง ๆ ของเขา แสดงการวิเคราะห์ประสิทธิภาพของตัวเลือกต่างๆ: ผสานคอลเลกชัน
BogeyMan

มันทำงานได้อย่างสมบูรณ์แบบ ใช้ concat () มากกว่าครึ่งล้านรายการซึ่งใช้เวลาหลายนาที วิธีนี้ใช้เวลาน้อยกว่า 5 วินาที
GreenFerret95

111

วิธีที่มีค่าใช้จ่ายในพื้นที่น้อยที่สุดคือการใช้วิธีการขยาย Concat

var combined = list1.Concat(list2);

มันจะสร้างอินสแตนซ์IEnumerable<T>ที่จะระบุองค์ประกอบของ list1 และ list2 ตามลำดับ


2
อย่าลืมว่าให้using System.Linq;ใช้ Concat
tothemario

43

สหภาพวิธีการอาจจะอยู่ที่ความต้องการของคุณ คุณไม่ได้ระบุว่าคำสั่งซื้อหรือรายการซ้ำนั้นสำคัญหรือไม่

ใช้ 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 ด้วย 'ยูเนี่ยน' คุณสามารถระบุตัวเปรียบเทียบซึ่งสามารถใช้เพื่อระบุว่ารายการควรจะรวมกันหรือไม่

แบบนี้:

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 ส่งคืนลำดับใหม่และในขณะที่แปลงผลลัพธ์เป็นรายการมันจะทำงานได้อย่างสมบูรณ์แบบ การแปลงโดยนัยอาจล้มเหลวบางครั้งเมื่อใช้วิธี 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

ฉันชอบสิ่งนี้เพราะมันไม่ได้กลายพันธุ์รายการใดรายการหนึ่ง
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

วิธีหนึ่งที่ฉันไม่เห็นมีการกล่าวถึงซึ่งอาจมีความทนทานมากกว่าโดยเฉพาะถ้าคุณต้องการเปลี่ยนองค์ประกอบในบางวิธี (เช่นคุณต้องการ.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 แปลแล้ว)
ขนาดของอาร์เรย์เกินช่วงที่สนับสนุน

การใช้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

ดังนั้นทุกอย่างทำงานได้ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.