Об'єднання двох масивів у .NET


225

Чи є вбудована функція в .NET 2.0, яка візьме два масиви і об'єднає їх в один масив?

Обидва масиви однотипні. Я отримую ці масиви з широко використовуваної функції в моїй кодовій базі і не можу змінити функцію повернення даних у іншому форматі.

Я хочу уникати написання власної функції, щоб досягти цього, якщо можливо.

Відповіді:


118

Якщо ви можете маніпулювати одним із масивів, ви можете змінити його розмір перед виконанням копії:

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

В іншому випадку ви можете створити новий масив

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

Детальніше про доступні методи масиву на MSDN .


1
Що з .NET 4.0, будь-які новини?
Шиммі Вайцхандлер

4
Зверніть увагу, що Array.Resizeмасив фактично не змінює, він копіює його. Ось чому перший параметр є по-реф (це означає, що ваш перший код, ймовірно, не буде компілюватися).
CodesInChaos

2
Я просто викину твій перший фрагмент коду. Він не пропонує переваги і важче читати ІМО.
CodesInChaos

3
Зверніть увагу, що порядок параметрів у другому прикладі коду для Array.Copy невірний. Використовуйте Array.Copy (array1, newArray, 0); замість цього.
marco birchler

Ви також можете зробити .. Список <байт> finalArray = новий список <байт> (); finalArray.AddRange (масив1); finalArray.AddRange (масив2); ==> finalArray.toArray ();
Седрик Бойвін

448

У C # 3.0 ви можете використовувати метод Concat LINQ для цього легко:

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

У C # 2.0 у вас немає такого прямого шляху, але Array.Copy - це, мабуть, найкраще рішення:

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

Це можна легко використати для реалізації вашої власної версії Concat.


1
Мені подобається ця реалізація LINQ. Мені справді потрібно зробити стрибок і незабаром потрапити в LINQ ...
GEOCHET

1
Річ, найкраща частина щодо впровадження LINQ - це не лише її лаконічність, вона також настільки ж ефективна, як і версія 2.0, оскільки вона працює проти IEnumerable.
Бред Вілсон

Ця відповідь включає цей спосіб, а також дає деякі результати бенчмаркінгу: stackoverflow.com/questions/415291/…
Демір

Це, мабуть, найпростіший спосіб, але це не буде ефективно для великих масивів, оскільки Concat реалізований за допомогою циклів foreach + урожайності (див. Довідкове джерело). Рішення з BlockCopy буде швидше.
tigrou

1
Лише невелика голова вгору: якщо ви хочете лише повторити комбінований результат, не потрібно перетворювати його в масив. Ця завершальна операція виконує копію масиву. Це не доведеться робити, якщо ви повторите через IEnumerable <int>. Звичайно, можуть бути вагомі причини, щоб це був масив.
Йонас

82

Використовуйте LINQ :

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

Майте на увазі, це видалить дублікати. Якщо ви хочете зберегти дублікати, використовуйте Concat.


132
ОБЕРЕЖНО: Union видалить дублікати.
Yogee

1
@Yogee, легко запам’ятати, як це в SQL, а також номенклатура пов'язана з теорією множин.
Zbigniew Wiadro

8
оскільки він видалить дублікати, це ніколи не може бути правильною відповіддю.
Роні Тові

1
Я бачу, як Саймон вже згадував питання про союз та альтернативний підхід, який він запропонував. Не потрібно далі обговорювати це, оскільки Саймон знає, що відповідає.
Sudhakar Chavali

41

Якщо ви не хочете видаляти дублікати, спробуйте це

Використовуйте LINQ:

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

11

По-перше, переконайтеся, що ви задаєте собі запитання "Чи справді я повинен тут використовувати масив"?

Якщо ви не будуєте щось, де швидкість має найважливіше значення, типовий список, як-от, List<int>це, мабуть, шлях. Єдиний раз, коли я коли-небудь використовую масиви, це масиви байтів при надсиланні матеріалів по мережі. Крім цього я їх ніколи не торкаюся.


Великий +1 тут. Зауважте, що найкраща практика полягає у тому, щоб уникнути викриття List<T>в публічних API: blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx
TrueWill

10

Простіше було б просто використовувати LINQ :

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

Спочатку перетворять масиви у списки та об'єднують їх ... Після цього просто перетворять список назад у масив :)


Ви не використовуєте масив безпосередньо. Ви використовували Список!
Бехзад Ебрагімі

7

Я думаю, ви можете використовувати Array.Copy для цього. Він займає індекс джерела та індекс призначення, тому ви зможете додати один масив до іншого. Якщо вам потрібно піти складніше, ніж просто додати одне до іншого, це може бути не правильним інструментом для вас.


5

Якщо припустити, що в масиві призначення достатньо місця, він Array.Copy()буде працювати. Ви також можете спробувати скористатись a List<T>та його .AddRange()методом.


4

Особисто я віддаю перевагу власним розширенням мов, які додаю або видаляю за бажанням для швидкого прототипування.

Далі наводиться приклад для рядків.

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

Це набагато швидше, ніж LINQ та Concat. Швидше все ж використовується спеціальна IEnumerableобгортка типу, яка зберігає посилання / покажчики пропущених масивів і дозволяє циклічно переглядати всю колекцію, як якщо б це був звичайний масив. (Корисно для HPC, графічної обробки, візуалізації графіки ...)

Ваш код:

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

Про весь код та версію для дженериків див: https://gist.github.com/lsauer/7919764

Примітка. Це повертає необмежений об'єкт IEnumerable. Повернути розширений об’єкт трохи повільніше.

Я збирав такі розширення з 2002 року, і багато кредитів було корисним людям у програмі CodeProject та «Stackoverflow». Незабаром я вийду і покладу посилання тут.


4

Усі вже говорили, але я вважаю, що це читабельніше, ніж підхід "використовувати як метод розширення":

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

Однак його можна використовувати лише при об'єднанні 2 масивів.


4

Це те, що я придумав. Працює для змінної кількості масивів.

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

Просто щоб це було зазначено як варіант: якщо масиви, з якими ви працюєте, мають примітивний тип - Boolean (bool), Char, SByte, Byte, Int16 (короткий), UInt16, Int32 (int), UInt32, Int64 (long ), UInt64, IntPtr, UIntPtr, Single або Double - тоді ви можете (або повинні?) Спробувати скористатися Buffer.BlockCopy . Відповідно до сторінки MSDN для класу Buffer :

Цей клас забезпечує кращу ефективність для маніпулювання примітивними типами, ніж аналогічні методи класу System.Array .

Використовуючи приклад C # 2.0 @ OwenP в відповідь в якості відправної точки, вона буде працювати наступним чином :

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

Різниця в синтаксисі між Buffer.BlockCopyі тим, Array.Copyщо використовується @OwenP, ледь не має, але це має бути швидше (навіть якщо лише незначно).


2

Якщо хтось інший шукає, як об’єднати два байтові масиви зображення:

        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

Ось простий приклад використання Array.CopyTo. Я думаю, що він відповідає на ваше запитання і наводить приклад використання CopyTo - я завжди спантеличений, коли мені потрібно використовувати цю функцію, оскільки довідка трохи незрозуміла - індекс - це позиція в масиві призначення, де відбувається вставлення.

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

Я думаю, ви не можете зрозуміти це набагато простіше.


1

Мені було потрібно рішення об’єднати невідому кількість масивів.

Здивований, що ніхто більше не запропонував рішення, використовуючи SelectManyс params.

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

Якщо ви не хочете чітких предметів, просто видаліть їх.

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

Примітка: Однозначно немає гарантії замовлення при використанні різних.


0

Я припускаю, що ви використовуєте власні типи масивів на відміну від вбудованих масивів .NET:

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

Іншим способом цього буде використання вбудованого класу ArrayList.

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 #.


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

Використовуючи вищезгаданий код, два масиви можна легко об'єднати.


0

Створений і розширений метод для обробки нуля

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

Якщо у вас є масив джерела в самому масиві, ви можете використовувати 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);   

дає

1
2
3
4
5
6

Можливо, це не найшвидший метод, але може підходити залежно від використання.


-1

Цей код буде працювати у всіх випадках:

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

Чи можете ви, будь ласка, додати докладніший опис пропонованого вами рішення?
абарізон

1
Хоча цей фрагмент коду може вирішити питання, у тому числі пояснення дійсно допомагає покращити якість вашої публікації. Пам'ятайте, що ви відповідаєте на запитання читачів у майбутньому, і ці люди можуть не знати причини вашої пропозиції щодо коду.
gunr2171

Це здається сортованим злиттям, яке, хоча й корисне саме по собі (головним чином як частина рекурсивної стратегії MergeSort), може бути більше, ніж цього вимагала ОП.
Даррель Гофман

Незважаючи на те, що це рішення працює, маючи безліч методик, доступних з моменту введення C # і VB.Net, люди можуть не віддавати перевагу таким рішенням.
Sudhakar Chavali

-2

Спробуйте це:

ArrayLIst al = new ArrayList();
al.AddRange(array_1);
al.AddRange(array_2);
al.AddRange(array_3);
array_4 = al.ToArray();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.