ฉันจะต่อสองอาร์เรย์ใน C # ได้อย่างไร


267
int[] x = new int [] { 1, 2, 3};
int[] y = new int [] { 4, 5 };

int[] z = // your answer here...

Debug.Assert(z.SequenceEqual(new int[] { 1, 2, 3, 4, 5 }));

ตอนนี้ฉันใช้

int[] z = x.Concat(y).ToArray();

มีวิธีที่ง่ายกว่าหรือมีประสิทธิภาพมากกว่านี้หรือไม่?


8
"ประสิทธิภาพ" หมายถึงอะไร รหัสสั้นพอที่จะเป็นดังนั้นฉันคิดว่าคุณหมายถึงมีประสิทธิภาพในแง่ของ CPU / RAM?
TToni

4
ไม่ดูอย่างรวดเร็วกับการแสดงสะท้อนว่ามันใช้บัฟเฟอร์คู่เมื่อเต็มรูปแบบ
erikkallen

ชัดเจนว่าฉันต้องการให้ z เป็นชนิด int []
hwiechers

4
ฉันไม่ได้กังวลเกี่ยวกับประสิทธิภาพจริงๆ (ฉันพูดได้ง่ายขึ้นหรือมีประสิทธิภาพมากขึ้น) ฉันถามคำถามเพื่อตรวจสอบว่าคนอื่นจัดการงานทั่วไปนี้อย่างไร
hwiechers

คำตอบ:


331
var z = new int[x.Length + y.Length];
x.CopyTo(z, 0);
y.CopyTo(z, x.Length);

8
@manthrax -> ในการป้องกัน C # มีแนวโน้มที่จะสนับสนุนรายการที่มีประสิทธิภาพมากกว่าอาเรย์ ดูเหมือนว่าจุดประสงค์การทำงานเพียงอย่างเดียวสำหรับการใช้อาร์เรย์คือสำหรับการโทรแบบ Interop (Unmanaged C ++)
Levi Fuller

@LeviFuller สถานที่อื่นที่ C # ใช้อาร์เรย์อยู่กับparamsพารามิเตอร์หมายเลขตัวแปร
ChrisW

1
@LeviFuller - เป็นเรื่องแปลกที่ระบบหลาย ๆ งานจะส่งคืนอาร์เรย์แทนรายการ เช่นSystem.IO.Directory.GetFiles()ส่งกลับอาร์เรย์ของสตริง
orion elenzil

4
นี่ไม่ใช่เรื่องแปลก Array นั้นไม่เปลี่ยนรูปรายการไม่ได้ นอกจากนี้ List จะใช้หน่วยความจำมากกว่าอาร์เรย์ยกเว้นว่ามีการเรียกว่า TrimExcess (ซึ่งไม่ได้เกิดขึ้นใน ToList)
CSharpie

1
อาเรย์ยังเร็วกว่ารายการเมื่อเข้าถึงข้อมูลเพราะลิสต์จะล้อมอาร์เรย์ด้านในและมีโอเวอร์เฮดสำหรับการเรียกดรรชนี
C0DEF52

84

ลองสิ่งนี้:

List<int> list = new List<int>();
list.AddRange(x);
list.AddRange(y);
int[] z = list.ToArray();

5
หรือแม้กระทั่งList<int> list = new List<int>(x);
Matthew Scharley

7
มันมีประสิทธิภาพมากกว่า x.Concat (y) อย่างไร มันได้ผลและทั้งหมดฉันแค่สงสัยว่ามีบางอย่างที่ทำให้ดีขึ้นหรือไม่
Mike Two

7
คุณอาจต้องการสร้างรายการบรรทัดแรก <int> list = new List <int> (x.Length + y.Length); เพื่อหลีกเลี่ยงการปรับขนาดที่อาจเกิดขึ้นในขณะที่คุณโทร AddRange
Mike Two

5
@Mathew Scharley คำถามคือการหาทางออกที่มีประสิทธิภาพมากขึ้น ฉันรู้ว่าชื่อทำให้ดูเหมือนว่าชุดค่าผสมเก่า ๆ จะทำ แต่คำถามเต็มไปกว่านั้น การอ่านคำตอบบางอย่างฉันรู้สึกว่าบางคนกำลังตอบคำถาม ดังนั้นฉันคิดว่าคำตอบนี้น่าจะพูดถึงประสิทธิภาพถ้าสมควรได้รับ upvotes เนื่องจากดูเหมือนจะเป็นประเด็นของคำถาม
Mike Two

2
ปรากฎว่า AddRange เป็นกระบวนการที่ค่อนข้างแพงดังนั้นคำตอบแรกในบอร์ดนี้ควรเป็นวิธีที่ต้องการ: dotnetperls.com/insertrange
Liam

49

คุณสามารถเขียนวิธีการขยาย:

public static T[] Concat<T>(this T[] x, T[] y)
{
    if (x == null) throw new ArgumentNullException("x");
    if (y == null) throw new ArgumentNullException("y");
    int oldLen = x.Length;
    Array.Resize<T>(ref x, x.Length + y.Length);
    Array.Copy(y, 0, x, oldLen, y.Length);
    return x;
}

แล้ว:

int[] x = {1,2,3}, y = {4,5};
int[] z = x.Concat(y); // {1,2,3,4,5}

1
ไม่ได้มีวิธีการขยายที่ทำงานบน IEnumerable ใด ๆ ?
Mike Two

2
ใช่และฉันจะใช้มันอย่างมีความสุขสำหรับกรณีส่วนใหญ่ แต่พวกเขามีค่าใช้จ่ายมากมาย มันขึ้นอยู่กับ; 98% ของเวลาที่ค่าโสหุ้ยดี ถ้าคุณอยู่ใน 2% แล้วงาน memcopy / array โดยตรงบางงานก็มีประโยชน์
Marc Gravell

1
@nawfal วิธีคือCopyเร็วกว่าCopyTo? สนใจที่จะทำอย่างละเอียด?
skrebbel

1
@skrebbel mine เป็นความคิดเห็นที่ไม่ถูกต้อง ฉันทำการทดสอบในตอนนั้นและพบว่าการคัดลอกเร็วขึ้น แต่ตอนนี้ดูเหมือนว่าพวกเขาจะเท่ากัน สิ่งที่ฉันอาจพบได้ในตอนนั้นอาจเป็นได้ว่าโดยรวมแล้ววิธีการของ Marc นั้นมีประสิทธิภาพมากกว่าเนื่องจากเขาส่งตัวอย่างเดียวกันกลับมาในขณะที่แนวทางของ Zed เขากำลังสร้างอาร์เรย์ใหม่ ขอโทษ :)
nawfal

1
@Shimmy มันจะไม่ ภายในเมธอด x นี้เป็นเพียงตัวแปรโลคัลการส่ง x เป็นวิธีอ้างอิงถึงการปรับขนาดจะสร้างอาร์เรย์ใหม่และแก้ไข (ตัวแปรโลคอล) x เพื่อชี้ไปที่มัน หรือเพื่อใช้ถ้อยคำใหม่: x ส่งผ่านไปยังการปรับขนาดและ x ภายในวิธีการขยายเป็นตัวแปรเดียวกัน แต่ x ไม่ได้ถูกส่งผ่านไปยังวิธีการขยายเป็นอ้างอิงดังนั้น x จึงเป็นตัวแปรที่แตกต่างจากตัวแปรในขอบเขตที่ส่วนขยายนี้ถูกเรียกจาก .
AnorZaken

40

ฉันตัดสินโซลูชันที่มีวัตถุประสงค์ทั่วไปเพิ่มเติมซึ่งอนุญาตให้ต่อชุดอาร์เรย์หนึ่งมิติในประเภทเดียวกันโดยพลการ (ฉันกำลังต่อกัน 3+ ครั้ง)

ฟังก์ชั่นของฉัน:

    public static T[] ConcatArrays<T>(params T[][] list)
    {
        var result = new T[list.Sum(a => a.Length)];
        int offset = 0;
        for (int x = 0; x < list.Length; x++)
        {
            list[x].CopyTo(result, offset);
            offset += list[x].Length;
        }
        return result;
    }

และการใช้งาน:

        int[] a = new int[] { 1, 2, 3 };
        int[] b = new int[] { 4, 5, 6 };
        int[] c = new int[] { 7, 8 };
        var y = ConcatArrays(a, b, c); //Results in int[] {1,2,3,4,5,6,7,8}

ฟังก์ชั่นที่ดีขอบคุณ! เปลี่ยนparams T[][]เป็นthis T[][]เพื่อให้เป็นส่วนขยาย
ทำเครื่องหมาย

สวัสดีพวกมันฟังก์ชั่นนี้ดูเหมือนสิ่งที่ฉันกำลังมองหา แต่ความคิดใดที่ฉันจะทำสิ่งนี้ได้? link @Mark
George B

31

นี่ไง:

using System.Linq;

int[] array1 = { 1, 3, 5 };
int[] array2 = { 0, 2, 4 };

// Concatenate array1 and array2.
var result1 = array1.Concat(array2);

4
คุณหมายความว่าint[] result = array1.ToList().Concat(array2.ToList()).toArray();คุณไม่สามารถสมัคร Concat กับอาร์เรย์ได้โดยตรงฉันเชื่อ
Michail Michailidis

4
วิธีแก้ปัญหานี้ - z = x.Concat (y) - ถูกกล่าวถึงในคำถามเดิมข้างต้น
Jon Schneider

1
นี่คือสิ่งที่เกิดขึ้นหากไม่มีtoArray() Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<string>' to 'string[]'. An explicit conversion exists (are you missing a cast?)
Tibor Udvari

2
นี่ไม่ใช่คำตอบโดยตรง OP ขอให้int[] result = ?คุณมีการซ่อนปัญหาคำตอบของคุณที่อยู่เบื้องหลังของคุณvarในการที่ผลของคุณจะไม่ได้IEnumerable<int> int[](หนึ่งในเหตุผลที่ฉันไม่ชอบvarวิธีส่งคืน)
David S.

2
วิธีนี้เป็นวิธีที่ใช้ในคำถามดังนั้นคำตอบนี้ไม่มีข้อมูลใหม่และไม่มีการ.ToArray()เรียกรหัสนี้จะไม่ส่งคืนอาร์เรย์ที่แท้จริงดังนั้นจึงเป็นคำตอบที่ไม่ถูกต้อง
Mani Gandham

10

คุณสามารถใช้การ ToArray () โทรออกปิดท้าย มีเหตุผลที่คุณต้องการให้อาร์เรย์หลังจากการเรียกไปยัง Concat หรือไม่?

การเรียก Concat สร้างตัววนซ้ำผ่านทั้งสองอาร์เรย์ มันไม่ได้สร้างอาร์เรย์ใหม่ดังนั้นคุณจึงไม่ได้ใช้หน่วยความจำเพิ่มเติมสำหรับอาร์เรย์ใหม่ เมื่อคุณเรียกใช้ ToArray คุณจะสร้างอาร์เรย์ใหม่ขึ้นมาและใช้หน่วยความจำสำหรับอาร์เรย์ใหม่

ดังนั้นถ้าคุณต้องการวนซ้ำอย่างง่าย ๆ ทั้งคู่ก็แค่โทรหา Concat


8

ฉันรู้ว่า OP เป็นเพียงเล็กน้อยที่อยากรู้อยากเห็นเกี่ยวกับประสิทธิภาพ อาร์เรย์ที่ใหญ่กว่านั้นอาจได้รับผลลัพธ์ที่แตกต่าง (ดู @kurdishTree) และมันมักจะไม่สำคัญ (@ jordan.peoples) ไม่น้อยฉันก็อยากรู้อยากเห็นและทำให้ใจของฉันหายไป (เนื่องจาก @TigerShark กำลังอธิบาย) .... ฉันหมายถึงฉันเขียนแบบทดสอบง่ายๆตามคำถามเดิม .... และคำตอบทั้งหมด ....

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace concat
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] x = new int [] { 1, 2, 3};
            int[] y = new int [] { 4, 5 };


            int itter = 50000;
            Console.WriteLine("test iterations: {0}", itter);

            DateTime startTest = DateTime.Now;
            for(int  i = 0; i < itter; i++)
            {
                int[] z;
                z = x.Concat(y).ToArray();
            }
            Console.WriteLine ("Concat Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks );

            startTest = DateTime.Now;
            for(int  i = 0; i < itter; i++)
            {
                var vz = new int[x.Length + y.Length];
                x.CopyTo(vz, 0);
                y.CopyTo(vz, x.Length);
            }
            Console.WriteLine ("CopyTo Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks );

            startTest = DateTime.Now;
            for(int  i = 0; i < itter; i++)
            {
                List<int> list = new List<int>();
                list.AddRange(x);
                list.AddRange(y);
                int[] z = list.ToArray();
            }
            Console.WriteLine("list.AddRange Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] z = Methods.Concat(x, y);
            }
            Console.WriteLine("Concat(x, y) Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] z = Methods.ConcatArrays(x, y);
            }
            Console.WriteLine("ConcatArrays Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] z = Methods.SSConcat(x, y);
            }
            Console.WriteLine("SSConcat Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int k = 0; k < itter; k++)
            {
                int[] three = new int[x.Length + y.Length];

                int idx = 0;

                for (int i = 0; i < x.Length; i++)
                    three[idx++] = x[i];
                for (int j = 0; j < y.Length; j++)
                    three[idx++] = y[j];
            }
            Console.WriteLine("Roll your own Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);


            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] z = Methods.ConcatArraysLinq(x, y);
            }
            Console.WriteLine("ConcatArraysLinq Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] z = Methods.ConcatArraysLambda(x, y);
            }
            Console.WriteLine("ConcatArraysLambda Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                List<int> targetList = new List<int>(x);
                targetList.Concat(y);
            }
            Console.WriteLine("targetList.Concat(y) Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] result = x.ToList().Concat(y.ToList()).ToArray();
            }
            Console.WriteLine("x.ToList().Concat(y.ToList()).ToArray() Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);
        }
    }
    static class Methods
    {
        public static T[] Concat<T>(this T[] x, T[] y)
        {
            if (x == null) throw new ArgumentNullException("x");
            if (y == null) throw new ArgumentNullException("y");
            int oldLen = x.Length;
            Array.Resize<T>(ref x, x.Length + y.Length);
            Array.Copy(y, 0, x, oldLen, y.Length);
            return x;
        }

        public static T[] ConcatArrays<T>(params T[][] list)
        {
            var result = new T[list.Sum(a => a.Length)];
            int offset = 0;
            for (int x = 0; x < list.Length; x++)
            {
                list[x].CopyTo(result, offset);
                offset += list[x].Length;
            }
            return result;
        }


        public static T[] SSConcat<T>(this T[] first, params T[][] arrays)
        {
            int length = first.Length;
            foreach (T[] array in arrays)
            {
                length += array.Length;
            }
            T[] result = new T[length];
            length = first.Length;
            Array.Copy(first, 0, result, 0, first.Length);
            foreach (T[] array in arrays)
            {
                Array.Copy(array, 0, result, length, array.Length);
                length += array.Length;
            }
            return result;
        }

        public static T[] ConcatArraysLinq<T>(params T[][] arrays)
        {
            return (from array in arrays
                    from arr in array
                    select arr).ToArray();
        }

        public static T[] ConcatArraysLambda<T>(params T[][] arrays)
        {
            return arrays.SelectMany(array => array.Select(arr => arr)).ToArray();
        }
    }

}

ผลลัพธ์คือ:

ป้อนคำอธิบายรูปภาพที่นี่

ม้วนชัยชนะของคุณเอง


ในความเป็นธรรมกับวิธีการที่ใช้วิธีการวิธีการอาจเพิ่ม 10,000 เห็บในระบบของฉัน
รวม

1
ฉันวิ่งรหัสของคุณในสตูดิโอภาพ 2013 ในโหมดการเปิดตัวและพบว่าถ้าอาร์เรย์การทดสอบไม่เป็นเช่นนั้นเล็ก ๆ เป็นของคุณ (เช่นองค์ประกอบ 1000) CopyToจะ fasest และ ~ 3x Roll your ownเร็วกว่า
Mr. Ree

@ Mr.Ree ใช่อาร์เรย์ของฉันมันเล็กจริง ๆ ใช่ไหม ขอบคุณ จะสนใจดูว่าการคัดลอกบล็อกทำได้ดียิ่งขึ้นหรือไม่ ...
รวมกัน

7

ระวังด้วยConcatวิธีการ การต่อข้อมูลอาเรย์โพสต์ใน C #อธิบายว่า:

var z = x.Concat(y).ToArray();

จะไม่มีประสิทธิภาพสำหรับอาร์เรย์ขนาดใหญ่ นั่นหมายถึงConcatวิธีนี้ใช้สำหรับอาร์เรย์ขนาด meduim เท่านั้น (มากถึง 10,000 องค์ประกอบ)


2
สิ่งที่ควรทำกับอาร์เรย์ที่มีองค์ประกอบมากกว่า 10,000 รายการ?
alex

6
public static T[] Concat<T>(this T[] first, params T[][] arrays)
{
    int length = first.Length;
    foreach (T[] array in arrays)
    {
        length += array.Length;
    }
    T[] result = new T[length];
    length = first.Length;
    Array.Copy(first, 0, result, 0, first.Length);
    foreach (T[] array in arrays)
    {
        Array.Copy(array, 0, result, length, array.Length);
        length += array.Length;
    }
    return result;
}

2
ที่ StackOverflow โปรดอย่าเพิ่งวางโค้ด แต่อธิบายวิธีการของคุณด้วย ในกรณีเฉพาะนี้คุณอาจต้องอธิบายว่าคำตอบล่าช้าของคุณเพิ่มไปยังคำตอบที่ได้รับ (และได้รับการยอมรับ)
Gert Arnold

1
ไม่แน่ใจว่าสิ่งนี้ "ทำ" ทำก่อน param แรก แต่สำหรับส่วนที่เหลือนี่เป็นฟังก์ชันที่ยอดเยี่ยม ทั่วไปและมีพารามิเตอร์จำนวนไม่ จำกัด
Nyerguds

2
สวัสดี Nyerguds เพื่อตอบคำถามของคุณคำหลัก "นี้" ใช้เพื่อทำให้ฟังก์ชันเป็นวิธีส่วนขยาย สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการขยายโปรดดูบทความ MSDN นี้
JFish222

6

มีประสิทธิภาพมากขึ้น (เร็วกว่า) ที่จะใช้ Buffer.BlockCopyมากกว่าArray.CopyTo,

int[] x = new int [] { 1, 2, 3};
int[] y = new int [] { 4, 5 };

int[] z = new int[x.Length + y.Length];
var byteIndex = x.Length * sizeof(int);
Buffer.BlockCopy(x, 0, z, 0, byteIndex);
Buffer.BlockCopy(y, 0, z, byteIndex, y.Length * sizeof(int));

ฉันเขียนโปรแกรมทดสอบอย่างง่าย ๆ ที่ "อุ่นเครื่องกระวนกระวายใจ" รวบรวมในโหมดการเปิดตัวและวิ่งได้โดยไม่ต้องติดตั้งดีบักเกอร์บนเครื่องของฉัน

สำหรับ 10,000,000 ซ้ำของตัวอย่างในคำถาม

Concat ใช้เวลา 3088ms

CopyTo ใช้เวลา 1079 มิลลิวินาที

BlockCopy ใช้เวลา 603 มิลลิวินาที

ถ้าฉันเปลี่ยนชุดทดสอบเป็นสองชุดจาก 0 ถึง 99 แล้วฉันจะได้ผลลัพธ์เช่นนี้

Concat ใช้เวลา 45945 มิลลิวินาที

CopyTo ใช้เวลา 2230 มิลลิวินาที

BlockCopy ใช้เวลา 1689 มิลลิวินาที

จากผลลัพธ์เหล่านี้ฉันสามารถยืนยันได้ว่าCopyToและBlockCopyวิธีการนั้นมีประสิทธิภาพมากกว่าConcatและยิ่งกว่านั้นหากประสิทธิภาพเป็นเป้าหมายBlockCopyมีค่ามากกว่าCopyToมีค่ามากกว่า

หากต้องการทราบคำตอบนี้หากประสิทธิภาพไม่สำคัญหรือมีการวนซ้ำสองสามครั้งให้เลือกวิธีที่คุณค้นหาได้ง่ายที่สุด Buffer.BlockCopyให้ประโยชน์บางอย่างสำหรับการแปลงประเภทเกินขอบเขตของคำถามนี้


6

ตอบล่าช้า :-)

public static class ArrayExtention
    {

        public static T[] Concatenate<T>(this T[] array1, T[] array2)
        {
            T[] result = new T[array1.Length + array2.Length];
            array1.CopyTo(result, 0);
            array2.CopyTo(result, array1.Length);
            return result;
        }

    }

3

โครงสร้างที่มีประสิทธิภาพมากที่สุดในแง่ของ RAM (และ CPU) เพื่อเก็บอาร์เรย์ที่รวมกันจะเป็นคลาสพิเศษที่ใช้ IEnumerable (หรือหากคุณต้องการแม้จะมาจาก Array) และเชื่อมโยงภายในกับอาร์เรย์ดั้งเดิมเพื่ออ่านค่า AFAIK Concat ทำอย่างนั้น

ในโค้ดตัวอย่างของคุณคุณสามารถละเว้น. ToArray () ได้ซึ่งจะทำให้มีประสิทธิภาพมากขึ้น


3

ขออภัยที่จะชุบชีวิตด้ายเก่า แต่วิธีนี้:

static IEnumerable<T> Merge<T>(params T[][] arrays)
{
    var merged = arrays.SelectMany(arr => arr);

    foreach (var t in merged)
        yield return t;
}

จากนั้นในรหัสของคุณ:

int[] x={1, 2, 3};
int[] y={4, 5, 6};

var z=Merge(x, y);  // 'z' is IEnumerable<T>

var za=z.ToArray(); // 'za' is int[]

จนกว่าคุณจะโทร.ToArray(), .ToList()หรือ.ToDictionary(...)หน่วยความจำที่ไม่ได้รับการจัดสรรคุณมีอิสระที่จะ "สร้างแบบสอบถามของคุณ" และอีกสายหนึ่งอย่างใดอย่างหนึ่งของผู้ที่สามที่จะดำเนินการหรือเพียงแค่ไปผ่านพวกเขาทั้งหมดโดยใช้foreach (var i in z){...}ข้อซึ่งจะส่งกลับรายการได้ตลอดเวลาจากที่yield return t;ข้างบน...

ฟังก์ชั่นข้างต้นสามารถทำให้เป็นส่วนขยายดังนี้:

static IEnumerable<T> Merge<T>(this T[] array1, T[] array2)
{
    var merged = array1.Concat(array2);

    foreach (var t in merged)
        yield return t;
}

ดังนั้นในรหัสคุณสามารถทำสิ่งที่ชอบ:

int[] x1={1, 2, 3};
int[] x2={4, 5, 6};
int[] x3={7, 8};

var z=x1.Merge(x2).Merge(x3);   // 'z' is IEnumerable<T>

var za=z.ToArray(); // 'za' is int[]

ที่เหลือก็เหมือนเดิม

การปรับปรุงอื่น ๆ นี้จะเปลี่ยนT[]เป็นIEnumerable<T>(ดังนั้นparams T[][]จะกลายเป็นparams IEnumerable<T>[] ) เพื่อให้ฟังก์ชั่นเหล่านี้ยอมรับมากกว่าเพียงแค่อาร์เรย์

หวังว่านี่จะช่วยได้


2

คุณสามารถทำได้ในแบบที่คุณอ้างถึงหรือถ้าคุณต้องการได้รับคู่มือจริงๆเกี่ยวกับมันคุณสามารถหมุนวนของคุณเอง:

        string[] one = new string[] { "a", "b" };
        string[] two = new string[] { "c", "d" };
        string[] three;

        three = new string[one.Length + two.Length];

        int idx = 0;

        for (int i = 0; i < one.Length; i++)
            three[idx++] = one[i];
        for (int j = 0; j < two.Length; j++)
            three[idx++] = two[j];

@nawfal ฉันคิดว่ามันจะขึ้นอยู่กับขนาดของอาร์เรย์ นี่ชนะการทดสอบอาร์เรย์ขนาดเล็กของฉัน
รวม

2

ฉันพบโซลูชันบรรทัดเดียวที่หรูหราโดยใช้LINQหรือนิพจน์แลมบ์ดาทั้งสองทำงานเหมือนกัน (LINQ ถูกแปลงเป็นแลมบ์ดาเมื่อคอมไพล์โปรแกรม) การแก้ปัญหาทำงานได้กับทุกประเภทอาเรย์และจำนวนของอาร์เรย์ใด ๆ

ใช้ LINQ:

public static T[] ConcatArraysLinq<T>(params T[][] arrays)
{
    return (from array in arrays
            from arr in array
            select arr).ToArray();
}

การใช้แลมบ์ดา:

public static T[] ConcatArraysLambda<T>(params T[][] arrays)
{
    return arrays.SelectMany(array => array.Select(arr => arr)).ToArray();
}

ฉันให้ทั้งความชอบ การแก้ปัญหาที่ชาญฉลาด@Sergey Shteynหรือ@ deepee1นั้นเร็วขึ้นเล็กน้อยการแสดงออกของแลมบ์ดานั้นช้าที่สุด เวลาที่ใช้นั้นขึ้นอยู่กับประเภทขององค์ประกอบอาเรย์ แต่ถ้ามีการโทรนับล้านครั้งจะไม่มีความแตกต่างอย่างมีนัยสำคัญระหว่างเมธอด


1

สิ่งที่คุณต้องจำไว้คือเมื่อใช้ LINQ คุณจะใช้การดำเนินการล่าช้า วิธีอื่น ๆ ที่อธิบายไว้ที่นี่ทั้งหมดทำงานได้อย่างสมบูรณ์ แต่พวกเขาจะถูกดำเนินการทันที นอกจากนี้ฟังก์ชั่น Concat () อาจได้รับการปรับให้เหมาะสมที่สุดในแบบที่คุณไม่สามารถทำได้ (การโทรไปยัง API ภายใน, การโทรในระบบปฏิบัติการ ฯลฯ ) อย่างไรก็ตามถ้าคุณจำเป็นต้องลองและเพิ่มประสิทธิภาพจริงๆคุณกำลังเดินทางไปที่ "รากเหง้าแห่งความชั่ว";)



1

นี่คือคำตอบของฉัน:

int[] z = new List<string>()
    .Concat(a)
    .Concat(b)
    .Concat(c)
    .ToArray();

วิธีนี้สามารถนำมาใช้ในระดับเริ่มต้นตัวอย่างเช่นการกำหนด concatenation คงที่ของอาร์เรย์คงที่:

public static int[] a = new int [] { 1, 2, 3, 4, 5 };
public static int[] b = new int [] { 6, 7, 8 };
public static int[] c = new int [] { 9, 10 };

public static int[] z = new List<string>()
    .Concat(a)
    .Concat(b)
    .Concat(c)
    .ToArray();

อย่างไรก็ตามมันมาพร้อมกับสองประการที่คุณต้องพิจารณา:

  • Concat วิธีการสร้าง iterator ทั้งอาร์เรย์: มันไม่ได้สร้างอาร์เรย์ใหม่จึงมีประสิทธิภาพในแง่ของการใช้หน่วยความจำ: แต่ภายหลัง ToArray  จะลบล้างข้อได้เปรียบดังกล่าวเพราะมันจริงจะสร้างอาร์เรย์ใหม่และใช้หน่วยความจำสำหรับ อาร์เรย์ใหม่
  • ดังที่ @Jodrell กล่าวว่าConcatจะค่อนข้างไม่มีประสิทธิภาพสำหรับอาร์เรย์ขนาดใหญ่: ควรใช้สำหรับอาร์เรย์ขนาดกลางเท่านั้น

หากการเล็งเป้าหมายเพื่อประสิทธิภาพเป็นสิ่งจำเป็นต้องใช้วิธีการต่อไปนี้แทน:

/// <summary>
/// Concatenates two or more arrays into a single one.
/// </summary>
public static T[] Concat<T>(params T[][] arrays)
{
    // return (from array in arrays from arr in array select arr).ToArray();

    var result = new T[arrays.Sum(a => a.Length)];
    int offset = 0;
    for (int x = 0; x < arrays.Length; x++)
    {
        arrays[x].CopyTo(result, offset);
        offset += arrays[x].Length;
    }
    return result;
}

หรือ (สำหรับแฟน ๆ หนึ่งสมุทร):

int[] z = (from arrays in new[] { a, b, c } from arr in arrays select arr).ToArray();

แม้ว่าวิธีการหลังจะสวยงามกว่า แต่วิธีการก่อนหน้านั้นดีกว่าสำหรับประสิทธิภาพอย่างแน่นอน

สำหรับข้อมูลเพิ่มเติมโปรดอ้างอิงโพสต์นี้ในบล็อกของฉัน


0

สำหรับ int [] สิ่งที่คุณทำดูดีสำหรับฉัน astander ของList<int>คำตอบก็จะทำงานได้ดีสำหรับ


2
Concat จะทำงานกับรายการ <int> ด้วย นั่นเป็นสิ่งที่ยอดเยี่ยมเกี่ยวกับ Concat มันใช้ได้กับ IEnumerable <> ใด ๆ
Mike Two

0

สำหรับอาร์เรย์ขนาดเล็ก <10,000 องค์ประกอบ:

using System.Linq;

int firstArray = {5,4,2};
int secondArray = {3,2,1};

int[] result = firstArray.ToList().Concat(secondArray.ToList()).toArray();

ทำไมต้องใช้ Linq เมื่อคุณไม่ต้องทำ!
ina

0
static class Extensions
{
    public static T[] Concat<T>(this T[] array1, params T[] array2) => ConcatArray(array1, array2);

    public static T[] ConcatArray<T>(params T[][] arrays)
    {
        int l, i;

        for (l = i = 0; i < arrays.Length; l += arrays[i].Length, i++);

        var a = new T[l];

        for (l = i = 0; i < arrays.Length; l += arrays[i].Length, i++)
            arrays[i].CopyTo(a, l);

        return a;
    }
}

ฉันคิดว่าวิธีแก้ปัญหาข้างต้นนั้นกว้างกว่า & เบากว่าที่ฉันเห็นที่นี่ มันเป็นเรื่องทั่วไปมากกว่าเพราะมันไม่ได้ จำกัด การเรียงต่อกันเพียงสองอาร์เรย์และเบากว่าเพราะมันไม่ได้ใช้ LINQ หรือ List

หมายเหตุโซลูชันมีความรัดกุมและ Generality ที่เพิ่มเข้ามาจะไม่เพิ่มโอเวอร์เฮดของรันไทม์ที่สำคัญ


ฉันขอแนะนำให้พยายามค้นหาคำถามที่ใหม่กว่าหรือคำถามที่คนอื่น ๆ ยังไม่มีคำตอบมากมาย - รวมถึงคำถามที่เหมือนคุณ
Andrew Barber

ฉันเสนอวิธีแก้ปัญหานี้เพราะฉันคิดว่ามันสรุปสิ่งที่ดีจากอีกข้อหนึ่ง มันถูกสร้างขึ้นมา
drowa

-2

int [] x = new int [] {1, 2, 3}; int [] y = new int [] {4, 5};

int [] z = x.Union (y) .ToArray ();


2
Unionไม่ใช่วิธีที่ดีในการทำเช่นนี้เนื่องจากเป็นการโทรDistinctและลบรายการที่ซ้ำซ้อนออกจากคอลเลกชันที่เข้าร่วม Concatดีกว่ามาก แต่มันมีอยู่แล้วในคำถามเดิม
nurchi

-3
int[] scores = { 100, 90, 90, 80, 75, 60 };
int[] alice = { 50, 65, 77, 90, 102 };
int[] scoreBoard = new int[scores.Length + alice.Length];

int j = 0;
for (int i=0;i<(scores.Length+alice.Length);i++)  // to combine two arrays
{
    if(i<scores.Length)
    {
        scoreBoard[i] = scores[i];
    }
    else
    {
        scoreBoard[i] = alice[j];
        j = j + 1;

    }
}


for (int l = 0; l < (scores.Length + alice.Length); l++)
{
    Console.WriteLine(scoreBoard[l]);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.