Saya memiliki 3 byte array di C # yang harus saya gabungkan menjadi satu. Apa metode yang paling efisien untuk menyelesaikan tugas ini?
Saya memiliki 3 byte array di C # yang harus saya gabungkan menjadi satu. Apa metode yang paling efisien untuk menyelesaikan tugas ini?
Jawaban:
Untuk tipe primitif (termasuk byte), gunakan System.Buffer.BlockCopysebagai ganti System.Array.Copy. Lebih cepat.
Saya menghitung setiap metode yang disarankan dalam satu loop yang dieksekusi 1 juta kali menggunakan 3 array masing-masing 10 byte. Inilah hasilnya:
System.Array.Copy - 0,2187556 detikSystem.Buffer.BlockCopy - 0,1406286 detikSaya meningkatkan ukuran setiap array menjadi 100 elemen dan menjalankan kembali tes:
System.Array.Copy - 0,2812554 detikSystem.Buffer.BlockCopy - 0,2500048 detikSaya meningkatkan ukuran setiap array menjadi 1000 elemen dan menjalankan kembali tes:
System.Array.Copy - 1,0781457 detikSystem.Buffer.BlockCopy - 1,0156445 detikAkhirnya, saya meningkatkan ukuran setiap array menjadi 1 juta elemen dan menjalankan ulang pengujian, mengeksekusi setiap loop hanya 4000 kali:
System.Array.Copy - 13,4533833 detikSystem.Buffer.BlockCopy - 13,1096267 detikJadi, jika Anda membutuhkan array byte baru, gunakan
byte[] rv = new byte[a1.Length + a2.Length + a3.Length];
System.Buffer.BlockCopy(a1, 0, rv, 0, a1.Length);
System.Buffer.BlockCopy(a2, 0, rv, a1.Length, a2.Length);
System.Buffer.BlockCopy(a3, 0, rv, a1.Length + a2.Length, a3.Length);
Tapi, jika Anda bisa menggunakan metode IEnumerable<byte>, PASTI lebih suka metode LINQ Concat <>. Ini hanya sedikit lebih lambat dari operator hasil C #, tetapi lebih ringkas dan lebih elegan.
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
Jika Anda memiliki jumlah array yang sewenang-wenang dan menggunakan .NET 3.5, Anda dapat membuat System.Buffer.BlockCopysolusinya lebih umum seperti ini:
private byte[] Combine(params byte[][] arrays)
{
byte[] rv = new byte[arrays.Sum(a => a.Length)];
int offset = 0;
foreach (byte[] array in arrays) {
System.Buffer.BlockCopy(array, 0, rv, offset, array.Length);
offset += array.Length;
}
return rv;
}
* Catatan: Blok di atas mengharuskan Anda menambahkan namespace berikut di bagian atas agar bisa berfungsi.
using System.Linq;
Untuk poin Jon Skeet mengenai iterasi dari struktur data berikutnya (byte array vs IEnumerable <byte>), saya menjalankan kembali tes waktu terakhir (1 juta elemen, 4000 iterasi), menambahkan loop yang mengulangi array penuh dengan masing-masing lulus:
System.Array.Copy - 78,20550510 detikSystem.Buffer.BlockCopy - 77,89261900 detikIntinya adalah, SANGAT penting untuk memahami efisiensi penciptaan dan penggunaan struktur data yang dihasilkan. Dengan hanya berfokus pada efisiensi penciptaan dapat mengabaikan inefisiensi yang terkait dengan penggunaan. Salam, Jon.
Banyak jawaban bagi saya tampaknya mengabaikan persyaratan yang dinyatakan:
Keduanya bersama-sama mengesampingkan urutan byte LINQ - apa pun dengan yieldakan membuat tidak mungkin untuk mendapatkan ukuran akhir tanpa iterasi melalui seluruh urutan.
Jika itu bukan persyaratan nyata tentunya, LINQ bisa menjadi solusi yang sangat baik (atau IList<T>implementasinya). Namun, saya akan berasumsi bahwa Superdumbell tahu apa yang dia inginkan.
(EDIT: Saya baru saja berpikir lain. Ada perbedaan besar antara membuat salinan array dan membacanya dengan malas. Pertimbangkan apa yang terjadi jika Anda mengubah data di salah satu array "sumber" setelah memanggil Combine(atau apa pun) ) tetapi sebelum menggunakan hasilnya - dengan evaluasi malas, perubahan itu akan terlihat. Dengan salinan langsung, itu tidak akan. Situasi yang berbeda akan memerlukan perilaku yang berbeda - hanya sesuatu yang harus diperhatikan.)
Berikut adalah metode yang saya usulkan - yang sangat mirip dengan yang terkandung dalam beberapa jawaban lain, tentu saja :)
public static byte[] Combine(byte[] first, byte[] second)
{
byte[] ret = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public static byte[] Combine(byte[] first, byte[] second, byte[] third)
{
byte[] ret = new byte[first.Length + second.Length + third.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
Buffer.BlockCopy(third, 0, ret, first.Length + second.Length,
third.Length);
return ret;
}
public static byte[] Combine(params byte[][] arrays)
{
byte[] ret = new byte[arrays.Sum(x => x.Length)];
int offset = 0;
foreach (byte[] data in arrays)
{
Buffer.BlockCopy(data, 0, ret, offset, data.Length);
offset += data.Length;
}
return ret;
}
Tentu saja versi "params" membutuhkan pembuatan array array byte terlebih dahulu, yang memperkenalkan inefisiensi tambahan.
Saya mengambil contoh Matt's LINQ satu langkah lebih jauh untuk kebersihan kode:
byte[] rv = a1.Concat(a2).Concat(a3).ToArray();
Dalam kasus saya, arraynya kecil, jadi saya tidak peduli dengan kinerja.
Jika Anda hanya perlu array byte baru, gunakan yang berikut ini:
byte[] Combine(byte[] a1, byte[] a2, byte[] a3)
{
byte[] ret = new byte[a1.Length + a2.Length + a3.Length];
Array.Copy(a1, 0, ret, 0, a1.Length);
Array.Copy(a2, 0, ret, a1.Length, a2.Length);
Array.Copy(a3, 0, ret, a1.Length + a2.Length, a3.Length);
return ret;
}
Atau, jika Anda hanya perlu satu IEnumerable, pertimbangkan untuk menggunakan operator hasil C # 2.0:
IEnumerable<byte> Combine(byte[] a1, byte[] a2, byte[] a3)
{
foreach (byte b in a1)
yield return b;
foreach (byte b in a2)
yield return b;
foreach (byte b in a3)
yield return b;
}
Saya benar-benar mengalami beberapa masalah dengan menggunakan Concat ... (dengan array dalam 10-juta, itu benar-benar macet).
Saya menemukan berikut ini menjadi sederhana, mudah dan bekerja dengan cukup baik tanpa menabrak saya, dan berfungsi untuk jumlah array APAPUN (bukan hanya tiga) (Menggunakan LINQ):
public static byte[] ConcatByteArrays(params byte[][] arrays)
{
return arrays.SelectMany(x => x).ToArray();
}
Kelas memorystream melakukan pekerjaan ini dengan cukup baik untuk saya. Saya tidak bisa menjalankan kelas buffer secepat memorystream.
using (MemoryStream ms = new MemoryStream())
{
ms.Write(BitConverter.GetBytes(22),0,4);
ms.Write(BitConverter.GetBytes(44),0,4);
ms.ToArray();
}
public static bool MyConcat<T>(ref T[] base_arr, ref T[] add_arr)
{
try
{
int base_size = base_arr.Length;
int size_T = System.Runtime.InteropServices.Marshal.SizeOf(base_arr[0]);
Array.Resize(ref base_arr, base_size + add_arr.Length);
Buffer.BlockCopy(add_arr, 0, base_arr, base_size * size_T, add_arr.Length * size_T);
}
catch (IndexOutOfRangeException ioor)
{
MessageBox.Show(ioor.Message);
return false;
}
return true;
}
where T : struct), tetapi - tidak menjadi ahli dalam jeroan CLR - saya tidak bisa mengatakan apakah Anda mungkin mendapatkan pengecualian pada struct tertentu juga (mis. jika mengandung bidang tipe referensi)
public static byte[] Concat(params byte[][] arrays) {
using (var mem = new MemoryStream(arrays.Sum(a => a.Length))) {
foreach (var array in arrays) {
mem.Write(array, 0, array.Length);
}
return mem.ToArray();
}
}
Dapat menggunakan obat generik untuk menggabungkan array. Kode berikut dapat dengan mudah diperluas ke tiga array. Dengan cara ini Anda tidak perlu menduplikasi kode untuk berbagai jenis array. Beberapa jawaban di atas tampaknya terlalu rumit bagi saya.
private static T[] CombineTwoArrays<T>(T[] a1, T[] a2)
{
T[] arrayCombined = new T[a1.Length + a2.Length];
Array.Copy(a1, 0, arrayCombined, 0, a1.Length);
Array.Copy(a2, 0, arrayCombined, a1.Length, a2.Length);
return arrayCombined;
}
Inilah generalisasi jawaban yang diberikan oleh @Jon Skeet. Pada dasarnya sama, hanya dapat digunakan untuk semua jenis array, tidak hanya byte:
public static T[] Combine<T>(T[] first, T[] second)
{
T[] ret = new T[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public static T[] Combine<T>(T[] first, T[] second, T[] third)
{
T[] ret = new T[first.Length + second.Length + third.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
Buffer.BlockCopy(third, 0, ret, first.Length + second.Length,
third.Length);
return ret;
}
public static T[] Combine<T>(params T[][] arrays)
{
T[] ret = new T[arrays.Sum(x => x.Length)];
int offset = 0;
foreach (T[] data in arrays)
{
Buffer.BlockCopy(data, 0, ret, offset, data.Length);
offset += data.Length;
}
return ret;
}
sizeof(...)dan mengalikannya dengan jumlah elemen yang ingin Anda salin, tetapi sizeof tidak dapat digunakan dengan tipe generik. Dimungkinkan - untuk beberapa jenis - untuk digunakan Marshal.SizeOf(typeof(T)), tetapi Anda akan mendapatkan kesalahan runtime dengan tipe tertentu (mis. String). Seseorang dengan pengetahuan yang lebih menyeluruh tentang cara kerja tipe CLR akan dapat menunjukkan semua kemungkinan jebakan di sini. Cukuplah untuk mengatakan bahwa menulis metode gabungan array generik [menggunakan BlockCopy] tidak sepele.
/// <summary>
/// Combine two Arrays with offset and count
/// </summary>
/// <param name="src1"></param>
/// <param name="offset1"></param>
/// <param name="count1"></param>
/// <param name="src2"></param>
/// <param name="offset2"></param>
/// <param name="count2"></param>
/// <returns></returns>
public static T[] Combine<T>(this T[] src1, int offset1, int count1, T[] src2, int offset2, int count2)
=> Enumerable.Range(0, count1 + count2).Select(a => (a < count1) ? src1[offset1 + a] : src2[offset2 + a - count1]).ToArray();
Yang Anda perlukan untuk melewati daftar Byte Arrays dan fungsi ini akan mengembalikan Anda Array Bytes (Digabung). Ini adalah solusi terbaik menurut saya :).
public static byte[] CombineMultipleByteArrays(List<byte[]> lstByteArray)
{
using (var ms = new MemoryStream())
{
using (var doc = new iTextSharp.text.Document())
{
using (var copy = new PdfSmartCopy(doc, ms))
{
doc.Open();
foreach (var p in lstByteArray)
{
using (var reader = new PdfReader(p))
{
copy.AddDocument(reader);
}
}
doc.Close();
}
}
return ms.ToArray();
}
}
Concat adalah jawaban yang tepat, tetapi karena alasan tertentu hal yang dikuasai adalah mendapatkan suara terbanyak. Jika Anda menyukai jawaban itu, mungkin Anda akan lebih menyukai solusi yang lebih umum ini:
IEnumerable<byte> Combine(params byte[][] arrays)
{
foreach (byte[] a in arrays)
foreach (byte b in a)
yield return b;
}
yang akan membiarkan Anda melakukan hal-hal seperti:
byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();