Hợp nhất hai mảng trong .NET


225

Có một chức năng tích hợp trong .NET 2.0 sẽ lấy hai mảng và hợp nhất chúng thành một mảng không?

Các mảng đều cùng loại. Tôi nhận được các mảng này từ một hàm được sử dụng rộng rãi trong cơ sở mã của mình và không thể sửa đổi hàm để trả về dữ liệu ở định dạng khác.

Tôi đang tìm cách tránh viết chức năng của riêng mình để thực hiện điều này nếu có thể.

Câu trả lời:


118

Nếu bạn có thể thao tác một trong các mảng, bạn có thể thay đổi kích thước của nó trước khi thực hiện sao chép:

T[] array1 = getOneArray();
T[] array2 = getAnotherArray();
int array1OriginalLength = array1.Length;
Array.Resize<T>(ref array1, array1OriginalLength + array2.Length);
Array.Copy(array2, 0, array1, array1OriginalLength, array2.Length);

Nếu không, bạn có thể tạo một mảng mới

T[] array1 = getOneArray();
T[] array2 = getAnotherArray();
T[] newArray = new T[array1.Length + array2.Length];
Array.Copy(array1, newArray, array1.Length);
Array.Copy(array2, 0, newArray, array1.Length, array2.Length);

Thêm về các phương thức Array có sẵn trên MSDN .


1
.NET 4.0 thì sao?
Shimmy Weitzhandler

4
Lưu ý rằng Array.Resizekhông thực sự thay đổi kích thước mảng, nó sao chép nó. Đó là lý do tại sao tham số đầu tiên là by-ref (có nghĩa là mã đầu tiên của bạn có thể sẽ không được biên dịch).
CodeInChaos

2
Tôi chỉ cần ném ra đoạn mã đầu tiên của bạn. Nó không mang lại lợi thế và khó đọc IMO hơn.
CodeInChaos

3
Xin lưu ý rằng thứ tự của các tham số trong ví dụ mã thứ hai cho Array.Copy là sai. Sử dụng Array.Copy (mảng1, newArray, 0); thay thế.
bạch dương marco

Bạn cũng có thể làm .. Danh sách <byte> FinalArray = Danh sách mới <byte> (); FinalArray.AddRange (mảng1); FinalArray.AddRange (mảng2); ==> FinalArray.toArray ();
Cédric Boivin

448

Trong C # 3.0, bạn có thể sử dụng phương pháp Concat của LINQ để thực hiện việc này một cách dễ dàng:

int[] front = { 1, 2, 3, 4 };
int[] back = { 5, 6, 7, 8 };
int[] combined = front.Concat(back).ToArray();

Trong C # 2.0, bạn không có cách trực tiếp như vậy, nhưng Array.Copy có lẽ là giải pháp tốt nhất:

int[] front = { 1, 2, 3, 4 };
int[] back = { 5, 6, 7, 8 };

int[] combined = new int[front.Length + back.Length];
Array.Copy(front, combined, front.Length);
Array.Copy(back, 0, combined, front.Length, back.Length);

Điều này có thể dễ dàng được sử dụng để thực hiện phiên bản của riêng bạn Concat.


1
Tôi thích việc thực hiện LINQ đó. Tôi thực sự cần phải thực hiện cú nhảy và sớm vào LINQ ...
GEOCHET

1
Giàu có, phần hay nhất về việc triển khai LINQ không chỉ là ngắn gọn, nó còn hiệu quả như phiên bản 2.0, vì nó hoạt động chống lại IEnumerable.
Brad Wilson

Câu trả lời này bao gồm cách này và cũng đưa ra một số kết quả đo điểm chuẩn: stackoverflow.com/questions/415291/iêu
Demir

Đây có lẽ là cách dễ nhất nhưng điều này sẽ không hiệu quả đối với các mảng lớn, vì Concat được triển khai bằng cách sử dụng các vòng lặp foreach + suất (xem nguồn tham khảo). Một giải pháp với BlockCopy sẽ nhanh hơn.
tigrou

1
Chỉ cần một cái đầu nhỏ lên: nếu bạn chỉ muốn lặp qua kết quả tổng hợp, không cần phải chuyển đổi nó thành một mảng. Hoạt động cuối cùng đó là một bản sao mảng. Nó sẽ không phải làm điều đó nếu bạn lặp qua một <int> IEnumerable. Tất nhiên, có thể có những lý do tốt để có nó là một mảng.
Jonas

82

Sử dụng LINQ :

var arr1 = new[] { 1, 2, 3, 4, 5 };
var arr2 = new[] { 6, 7, 8, 9, 0 };
var arr = arr1.Union(arr2).ToArray();

Hãy ghi nhớ, điều này sẽ loại bỏ trùng lặp. Nếu bạn muốn giữ các bản sao, hãy sử dụng Concat.


132
THẬN TRỌNG: Liên minh sẽ loại bỏ trùng lặp.
Yogee

1
@Yogee, dễ nhớ như trong SQL và danh pháp cũng được kết nối với lý thuyết tập hợp.
Zbigniew Wiadro

8
vì nó sẽ loại bỏ các bản sao, nó không bao giờ có thể là một câu trả lời thích hợp.
Roni Tovi

1
Tôi thấy Simon đã đề cập đến vấn đề Liên minh và cách tiếp cận thay thế mà anh ấy đề xuất. Không cần phải thảo luận thêm về điều đó vì Simon biết những gì anh ấy đang trả lời.
Sudhakar Chavali

41

Nếu bạn không muốn loại bỏ trùng lặp, thì hãy thử điều này

Sử dụng LINQ:

var arr1 = new[] { 1, 2, 3, 4, 5 };
var arr2 = new[] { 6, 7, 8, 9, 0 };
var arr = arr1.Concat(arr2).ToArray();

11

Trước tiên, hãy chắc chắn rằng bạn tự hỏi mình câu hỏi "Tôi có thực sự nên sử dụng Mảng ở đây không"?

Trừ khi bạn đang xây dựng một cái gì đó mà tốc độ là quan trọng nhất, một Danh sách được gõ, giống như List<int>có lẽ là cách để đi. Lần duy nhất tôi từng sử dụng mảng là cho mảng byte khi gửi nội dung qua mạng. Ngoài ra, tôi không bao giờ chạm vào chúng.


Lớn +1 ở đây. Lưu ý rằng thực hành tốt nhất là tránh tiếp xúc List<T>trong các API công cộng: blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx
TrueWill

10

Dễ dàng hơn chỉ là sử dụng LINQ :

var array = new string[] { "test" }.ToList();
var array1 = new string[] { "test" }.ToList();
array.AddRange(array1);
var result = array.ToArray();

Đầu tiên chuyển đổi các mảng thành danh sách và hợp nhất chúng ... Sau đó, chỉ cần chuyển đổi danh sách trở lại một mảng :)


Bạn không được sử dụng mảng trực tiếp. Bạn đã sử dụng Danh sách!
Behzad Ebrahimi

7

Tôi nghĩ bạn có thể sử dụng Array.Copy cho việc này. Nó lấy một chỉ mục nguồn và chỉ mục đích để bạn có thể nối thêm mảng này sang mảng khác. Nếu bạn cần đi phức tạp hơn là chỉ nối thêm cái này với cái kia, đây có thể không phải là công cụ phù hợp với bạn.



4

Cá nhân, tôi thích Phần mở rộng ngôn ngữ của riêng tôi, mà tôi thêm hoặc xóa theo ý muốn để tạo mẫu nhanh.

Sau đây là một ví dụ cho chuỗi.

//resides in IEnumerableStringExtensions.cs
public static class IEnumerableStringExtensions
{
   public static IEnumerable<string> Append(this string[] arrayInitial, string[] arrayToAppend)
   {
       string[] ret = new string[arrayInitial.Length + arrayToAppend.Length];
       arrayInitial.CopyTo(ret, 0);
       arrayToAppend.CopyTo(ret, arrayInitial.Length);

       return ret;
   }
}

Nó nhanh hơn nhiều so với LINQ và Concat. Vẫn nhanh hơn, đang sử dụng IEnumerabletrình bao bọc Kiểu tùy chỉnh lưu trữ các tham chiếu / con trỏ của các mảng đã qua và cho phép lặp trên toàn bộ bộ sưu tập như thể đó là một mảng bình thường. (Hữu ích trong HPC, Xử lý đồ họa, Kết xuất đồ họa ...)

Ma cua ban:

var someStringArray = new[]{"a", "b", "c"};
var someStringArray2 = new[]{"d", "e", "f"};
someStringArray.Append(someStringArray2 ); //contains a,b,c,d,e,f

Để biết toàn bộ mã và phiên bản generic, hãy xem: https://gist.github.com/lsauer/7919764

Lưu ý: Điều này trả về một đối tượng IEnumerable không được giám sát. Để trả về một đối tượng mở rộng là chậm hơn một chút.

Tôi đã biên soạn các tiện ích mở rộng như vậy từ năm 2002, với rất nhiều khoản tín dụng dành cho những người hữu ích trên CodeProject và 'Stackoverflow'. Tôi sẽ phát hành những điều này trong thời gian ngắn và đưa liên kết lên đây.


4

Mọi người đều đã có tiếng nói của mình nhưng tôi nghĩ điều này dễ đọc hơn phương pháp "sử dụng như phương pháp mở rộng":

var arr1 = new[] { 1, 2, 3, 4, 5 };
var arr2 = new[] { 6, 7, 8, 9, 0 };
var arr = Queryable.Concat(arr1, arr2).ToArray();

Tuy nhiên, nó chỉ có thể được sử dụng khi tập hợp 2 mảng.


4

Đây là những gì tôi đã đưa ra. Làm việc cho một số lượng khác nhau của mảng.

public static T[] ConcatArrays<T>(params T[][] args)
    {
        if (args == null)
            throw new ArgumentNullException();

        var offset = 0;
        var newLength = args.Sum(arr => arr.Length); 
        var newArray = new T[newLength];

        foreach (var arr in args)
        {
            Buffer.BlockCopy(arr, 0, newArray, offset, arr.Length);
            offset += arr.Length;
        }

        return newArray;
    }

...

var header = new byte[] { 0, 1, 2};
var data = new byte[] { 3, 4, 5, 6 };
var checksum = new byte[] {7, 0};
var newArray = ConcatArrays(header, data, checksum);
//output byte[9] { 0, 1, 2, 3, 4, 5, 6, 7, 0 }

3

Chỉ cần lưu ý là một tùy chọn: nếu các mảng bạn đang làm việc thuộc loại nguyên thủy - Boolean (bool), Char, SByte, Byte, Int16 (ngắn), UInt16, Int32 (int), UInt32, Int64 (dài ), UInt64, IntPtr, UIntPtr, Single hoặc Double - sau đó bạn có thể (hoặc nên?) Thử sử dụng Buffer.BlockCopy . Theo trang MSDN cho lớp Buffer :

Lớp này cung cấp hiệu năng tốt hơn để thao tác các kiểu nguyên thủy so với các phương thức tương tự trong lớp System.Array .

Sử dụng ví dụ C # 2.0 từ câu trả lời của @ OwenP làm điểm bắt đầu, nó sẽ hoạt động như sau:

int[] front = { 1, 2, 3, 4 };
int[] back = { 5, 6, 7, 8 };

int[] combined = new int[front.Length + back.Length];
Buffer.BlockCopy(front, 0, combined, 0, front.Length);
Buffer.BlockCopy(back, 0, combined, front.Length, back.Length);

Hầu như không có bất kỳ sự khác biệt nào về cú pháp giữa Buffer.BlockCopyArray.Copy@OwenP đã sử dụng, nhưng điều này sẽ nhanh hơn (ngay cả khi chỉ một chút).


2

Trong trường hợp người khác đang tìm cách hợp nhất hai mảng byte hình ảnh:

        private void LoadImage()
        {
            string src = string.empty;
            byte[] mergedImageData = new byte[0];

            mergedImageData = MergeTwoImageByteArrays(watermarkByteArray, backgroundImageByteArray);
            src = "data:image/png;base64," + Convert.ToBase64String(mergedImageData);
            MyImage.ImageUrl = src;
        }

        private byte[] MergeTwoImageByteArrays(byte[] imageBytes, byte[] imageBaseBytes)
        {
            byte[] mergedImageData = new byte[0];
            using (var msBase = new MemoryStream(imageBaseBytes))
            {
                System.Drawing.Image imgBase = System.Drawing.Image.FromStream(msBase);
                Graphics gBase = Graphics.FromImage(imgBase);
                using (var msInfo = new MemoryStream(imageBytes))
                {
                    System.Drawing.Image imgInfo = System.Drawing.Image.FromStream(msInfo);
                    Graphics gInfo = Graphics.FromImage(imgInfo);
                    gBase.DrawImage(imgInfo, new Point(0, 0));
                    //imgBase.Save(Server.MapPath("_____testImg.png"), ImageFormat.Png);
                    MemoryStream mergedImageStream = new MemoryStream();
                    imgBase.Save(mergedImageStream, ImageFormat.Png);
                    mergedImageData = mergedImageStream.ToArray();
                    mergedImageStream.Close();
                }
            }
            return mergedImageData;
        }

1

Đây là một ví dụ đơn giản sử dụng Array.CopyTo. Tôi nghĩ rằng nó trả lời câu hỏi của bạn và đưa ra một ví dụ về việc sử dụng CopyTo - Tôi luôn bối rối khi tôi cần sử dụng chức năng này vì sự trợ giúp hơi không rõ ràng - chỉ mục là vị trí trong mảng đích xảy ra chèn.

int[] xSrc1 = new int[3] { 0, 1, 2 };
int[] xSrc2 = new int[5] { 3, 4, 5, 6 , 7 };

int[] xAll = new int[xSrc1.Length + xSrc2.Length];
xSrc1.CopyTo(xAll, 0);
xSrc2.CopyTo(xAll, xSrc1.Length);

Tôi đoán bạn không thể có được nó đơn giản hơn nhiều.


1

Tôi cần một giải pháp để kết hợp một số mảng không xác định.

Ngạc nhiên không ai khác cung cấp một giải pháp sử dụng SelectManyvới params.

 private static T[] Combine<T>(params IEnumerable<T>[] items) =>
                    items.SelectMany(i => i).Distinct().ToArray();

Nếu bạn không muốn các mục riêng biệt chỉ cần loại bỏ khác biệt.

 public string[] Reds = new [] { "Red", "Crimson", "TrafficLightRed" };
 public string[] Greens = new [] { "Green", "LimeGreen" };
 public string[] Blues = new [] { "Blue", "SkyBlue", "Navy" };

 public string[] Colors = Combine(Reds, Greens, Blues);

Lưu ý: Chắc chắn không có gì đảm bảo cho việc đặt hàng khi sử dụng riêng biệt.


0

Tôi giả sử bạn đang sử dụng các loại mảng của riêng mình trái ngược với các mảng .NET tích hợp:

public string[] merge(input1, input2)
{
    string[] output = new string[input1.length + input2.length];
    for(int i = 0; i < output.length; i++)
    {
        if (i >= input1.length)
            output[i] = input2[i-input1.length];
        else
            output[i] = input1[i];
    }
    return output;
}

Một cách khác để làm điều này sẽ là sử dụng lớp ArrayList tích hợp.

public ArrayList merge(input1, input2)
{
    Arraylist output = new ArrayList();
    foreach(string val in input1)
        output.add(val);
    foreach(string val in input2)
        output.add(val);
    return output;
}

Cả hai ví dụ là C #.


0
int [] SouceArray1 = new int[] {2,1,3};
int [] SourceArray2 = new int[] {4,5,6};
int [] targetArray = new int [SouceArray1.Length + SourceArray2.Length];
SouceArray1.CopyTo(targetArray,0);
SourceArray2.CopyTo(targetArray,SouceArray1.Length) ; 
foreach (int i in targetArray) Console.WriteLine(i + " ");  

Sử dụng mã trên, hai Mảng có thể dễ dàng hợp nhất.


0

Phương thức được tạo và mở rộng để xử lý null

public static class IEnumerableExtenions
{
    public static IEnumerable<T> UnionIfNotNull<T>(this IEnumerable<T> list1, IEnumerable<T> list2)
    {
        if (list1 != null && list2 != null)
            return list1.Union(list2);
        else if (list1 != null)
            return list1;
        else if (list2 != null)
            return list2;
        else return null;
    }
}

0

Nếu bạn có các mảng nguồn trong một mảng, bạn có thể sử dụng SelectMany :

var arrays = new[]{new[]{1, 2, 3}, new[]{4, 5, 6}};
var combined = arrays.SelectMany(a => a).ToArray();
foreach (var v in combined) Console.WriteLine(v);   

cho

1
2
3
4
5
6

Có lẽ đây không phải là phương pháp nhanh nhất nhưng có thể phù hợp tùy thuộc vào usecase.


-1

Mã này sẽ hoạt động cho tất cả các trường hợp:

int[] a1 ={3,4,5,6};
int[] a2 = {4,7,9};
int i = a1.Length-1;
int j = a2.Length-1;
int resultIndex=  i+j+1;
Array.Resize(ref a2, a1.Length +a2.Length);
while(resultIndex >=0)
{
    if(i != 0 && j !=0)
    {
        if(a1[i] > a2[j])
        {
            a2[resultIndex--] = a[i--];
        }
        else
        {
            a2[resultIndex--] = a[j--];
        }
    }
    else if(i>=0 && j<=0)
    { 
        a2[resultIndex--] = a[i--];
    }
    else if(j>=0 && i <=0)
    {
       a2[resultIndex--] = a[j--];
    }
}

Bạn có thể vui lòng thêm mô tả về giải pháp bạn cung cấp?
abarisone

1
Mặc dù đoạn mã này có thể giải quyết câu hỏi, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
gunr2171

Đây dường như là một sự hợp nhất được sắp xếp, trong khi hữu ích theo cách riêng của nó (chủ yếu là một phần của chiến lược đệ quy MergeSort), có thể nhiều hơn OP yêu cầu.
Darrel Hoffman

Mặc dù giải pháp này hoạt động, có rất nhiều kỹ thuật có sẵn kể từ khi C # và VB.Net được giới thiệu, mọi người có thể không thích các giải pháp đó.
Sudhakar Chavali

-2

Thử cái này:

ArrayLIst al = new ArrayList();
al.AddRange(array_1);
al.AddRange(array_2);
al.AddRange(array_3);
array_4 = al.ToArray();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.