Слияние двух массивов в .NET

225

Есть ли в .NET 2.0 встроенная функция, которая будет принимать два массива и объединять их в один массив?

Массивы имеют одинаковый тип. Я получаю эти массивы из широко используемой функции в моей базе кода и не могу изменить функцию, чтобы она возвращала данные в другом формате.

Я стараюсь не писать свою собственную функцию для достижения этой цели, если это возможно.

kbrinley
источник

Ответы:

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

Подробнее о доступных методах Array на MSDN .

Блэр Конрад
источник
1
А как насчет .NET 4.0, есть новости?
Shimmy Weitzhandler
4
Обратите внимание, что на Array.Resizeсамом деле не изменяет размер массива, он копирует его. Вот почему первым параметром является by-ref (что означает, что ваш первый код, вероятно, не будет компилироваться).
CodesInChaos
2
Я бы просто выбросил твой первый кусок кода. Это не дает преимущества, и его сложнее читать по IMO.
CodesInChaos
3
Обратите внимание, что порядок параметров во втором примере кода для Array.Copy неправильный. Использовать Array.Copy (array1, newArray, 0); вместо.
Марко Берчлер
Вы также можете сделать .. List <byte> finalArray = new List <byte> (); 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.

OwenP
источник
1
Мне нравится эта реализация LINQ. Мне действительно нужно сделать прыжок и скоро попасть в LINQ ...
GEOCHET
1
Рич, лучшая часть реализации LINQ не только лаконична, но и эффективна, как версия 2.0, поскольку она работает с IEnumerable.
Брэд Уилсон
Этот ответ включает этот способ, а также дает некоторые результаты сравнительного анализа: stackoverflow.com/questions/415291/…
Демир
Это, вероятно, самый простой способ, но он не будет эффективным для больших массивов, поскольку Concat реализован с использованием циклов foreach + yield (см. Справочный источник). Решение с 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 удалит дубликаты.
Йоги
1
@ Йоге, легко запомнить, как в SQL, а также номенклатура связана с теорией множеств.
Збигнев Wiadro
8
поскольку он удалит дубликаты, он никогда не будет правильным ответом.
Рони Тови
1
Я вижу, Саймон уже упомянул вопрос о союзе и альтернативный подход, который он предложил. Не нужно обсуждать это дальше, так как Саймон знает, что он отвечает.
Судхакар Чавали
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>. Единственный раз, когда я использую массивы, это байтовые массивы при отправке данных по сети. Кроме этого, я никогда не прикасаюсь к ним.

CodesInChaos
источник
Большой +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 для этого. Он принимает исходный индекс и целевой индекс, так что вы сможете добавить один массив к другому. Если вам нужно выполнить более сложную задачу, чем просто добавить одно к другому, это может оказаться не подходящим инструментом для вас.

GEOCHET
источник
5

Предполагая, что массив назначения имеет достаточно места, Array.Copy()будет работать. Вы также можете попробовать использовать метод 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();

Однако его можно использовать только при объединении двух массивов.

Джон Рейли
источник
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 }
cj.burrow
источник
3

Просто чтобы это было отмечено как опция: если массивы, с которыми вы работаете, имеют примитивный тип - Boolean (bool), Char, SByte, Byte, Int16 (short), 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;
        }
Lukas
источник
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);

Я думаю, вы не можете сделать это намного проще.

pasx
источник
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);

Примечание. Определенно нет никаких гарантий при заказе.

Simon_Weaver
источник
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 #.

apandit
источник
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 + " ");  

Используя приведенный выше код, два массива можно легко объединить.

vikasse
источник
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

Вероятно, это не самый быстрый метод, но может подойти в зависимости от варианта использования.

schoetbi
источник
-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--];
    }
}
Раджкумар М
источник
Не могли бы вы добавить подробное описание предлагаемого решения?
abarisone
1
Хотя этот фрагмент кода может решить вопрос, в том числе объяснение действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин, по которым вы предлагаете код.
gunr2171
Похоже, что это отсортированное слияние, которое, хотя и полезно само по себе (в основном как часть рекурсивной стратегии MergeSort), может быть больше, чем запрашивал OP.
Даррел Хоффман
Несмотря на то, что это решение работает, имея много методов, доступных с момента появления C # и VB.Net, люди могут не предпочесть такие решения.
Судхакар Чавали
-2

Попробуй это:

ArrayLIst al = new ArrayList();
al.AddRange(array_1);
al.AddRange(array_2);
al.AddRange(array_3);
array_4 = al.ToArray();
Namco
источник