Tôi có 3 mảng byte trong C # mà tôi cần kết hợp thành một. Điều gì sẽ là phương pháp hiệu quả nhất để hoàn thành nhiệm vụ này?
Tôi có 3 mảng byte trong C # mà tôi cần kết hợp thành một. Điều gì sẽ là phương pháp hiệu quả nhất để hoàn thành nhiệm vụ này?
Câu trả lời:
Đối với các kiểu nguyên thủy (bao gồm byte), sử dụng System.Buffer.BlockCopy
thay vìSystem.Array.Copy
. Nó nhanh hơn.
Tôi đã tính thời gian cho mỗi phương thức được đề xuất trong một vòng lặp được thực hiện 1 triệu lần bằng cách sử dụng 3 mảng 10 byte mỗi phương thức. Đây là kết quả:
System.Array.Copy
- 0,2187556 giâySystem.Buffer.BlockCopy
- 0.1406286 giâyTôi đã tăng kích thước của mỗi mảng lên 100 phần tử và chạy lại bài kiểm tra:
System.Array.Copy
- 0,2812554 giâySystem.Buffer.BlockCopy
- 0,2500048 giâyTôi đã tăng kích thước của mỗi mảng lên 1000 phần tử và chạy lại bài kiểm tra:
System.Array.Copy
- 1.0781457 giâySystem.Buffer.BlockCopy
- 1.0156445 giâyCuối cùng, tôi đã tăng kích thước của mỗi mảng lên 1 triệu phần tử và chạy lại thử nghiệm, thực hiện mỗi vòng lặp chỉ 4000 lần:
System.Array.Copy
- 13,4533833 giâySystem.Buffer.BlockCopy
- 13.1096267 giâyVì vậy, nếu bạn cần một mảng byte mới, hãy sử dụng
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);
Nhưng, nếu bạn có thể sử dụng một phương thức IEnumerable<byte>
, DEFINITELY thích phương thức concat <> của LINQ. Nó chỉ chậm hơn một chút so với toán tử năng suất C #, nhưng ngắn gọn và thanh lịch hơn.
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
Nếu bạn có số lượng mảng tùy ý và đang sử dụng .NET 3.5, bạn có thể làm cho System.Buffer.BlockCopy
giải pháp chung chung hơn như thế này:
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;
}
* Lưu ý: Khối trên yêu cầu bạn thêm không gian tên sau ở trên cùng để nó hoạt động.
using System.Linq;
Theo quan điểm của Jon Skeet về việc lặp lại các cấu trúc dữ liệu tiếp theo (mảng byte so với IEnumerable <byte>), tôi đã chạy lại thử nghiệm thời gian cuối cùng (1 triệu phần tử, 4000 lần lặp), thêm một vòng lặp lặp lại trên toàn bộ mảng vượt qua:
System.Array.Copy
- 78.20550510 giâySystem.Buffer.BlockCopy
- 77,89261900 giâyVấn đề là, RẤT quan trọng để hiểu hiệu quả của cả việc tạo và sử dụng cấu trúc dữ liệu kết quả. Chỉ cần tập trung vào hiệu quả của việc tạo có thể bỏ qua sự không hiệu quả liên quan đến việc sử dụng. Kudos, Jon.
Nhiều câu trả lời dường như đang bỏ qua các yêu cầu đã nêu:
Cả hai cùng loại trừ một chuỗi byte LINQ - bất cứ điều gì yield
sẽ làm cho không thể có được kích thước cuối cùng mà không lặp lại trong toàn bộ chuỗi.
Nếu đó không phải là những yêu cầu thực sự của khóa học, LINQ có thể là một giải pháp hoàn toàn tốt (hoặc việc IList<T>
thực hiện). Tuy nhiên, tôi sẽ cho rằng Superdumbell biết anh ta muốn gì.
(EDIT: Tôi vừa có một suy nghĩ khác. Có một sự khác biệt lớn về ngữ nghĩa giữa việc tạo một bản sao của các mảng và đọc chúng một cách lười biếng. Hãy xem xét điều gì xảy ra nếu bạn thay đổi dữ liệu trong một trong các mảng "nguồn" sau khi gọi Combine
(hoặc bất cứ điều gì ) phương pháp nhưng trước khi sử dụng kết quả - với đánh giá lười biếng, sự thay đổi đó sẽ hiển thị. Với một bản sao ngay lập tức, nó sẽ không xảy ra. Các tình huống khác nhau sẽ đòi hỏi hành vi khác nhau - chỉ cần một cái gì đó để nhận biết.)
Dưới đây là các phương pháp được đề xuất của tôi - rất giống với các phương pháp có trong một số câu trả lời khác, chắc chắn :)
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;
}
Tất nhiên, phiên bản "params" yêu cầu tạo ra một mảng các mảng byte trước, điều này dẫn đến sự kém hiệu quả.
Tôi lấy ví dụ LINQ của Matt một bước nữa để làm sạch mã:
byte[] rv = a1.Concat(a2).Concat(a3).ToArray();
Trong trường hợp của tôi, các mảng là nhỏ, vì vậy tôi không quan tâm đến hiệu suất.
Nếu bạn chỉ cần một mảng byte mới, thì hãy sử dụng như sau:
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;
}
Ngoài ra, nếu bạn chỉ cần một IEnumerable duy nhất, hãy xem xét sử dụng toán tử năng suất 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;
}
Tôi thực sự gặp phải một số vấn đề khi sử dụng Concat ... (với các mảng trong 10 triệu, nó thực sự đã bị sập).
Tôi thấy những điều sau đây đơn giản, dễ dàng và hoạt động đủ tốt mà không gặp sự cố với tôi và nó hoạt động với BẤT K Array số mảng nào (không chỉ ba) (Nó sử dụng LINQ):
public static byte[] ConcatByteArrays(params byte[][] arrays)
{
return arrays.SelectMany(x => x).ToArray();
}
Lớp bộ nhớ thực hiện công việc này khá độc đáo đối với tôi. Tôi không thể có được lớp đệm để chạy nhanh như bộ nhớ.
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
), nhưng - không phải là chuyên gia trong các bộ phận của CLR - Tôi không thể nói liệu bạn có thể có ngoại lệ đối với các cấu trúc nhất định không (ví dụ: nếu chúng chứa các trường loại tham chiếu).
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();
}
}
Có thể sử dụng thuốc generic để kết hợp mảng. Mã sau có thể dễ dàng được mở rộng thành ba mảng. Bằng cách này, bạn không bao giờ cần phải sao chép mã cho các loại mảng khác nhau. Một số câu trả lời ở trên có vẻ quá phức tạp đối với tôi.
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;
}
Đây là một khái quát về câu trả lời được cung cấp bởi @Jon Skeet. Về cơ bản là giống nhau, chỉ có nó có thể sử dụng được cho bất kỳ loại mảng nào, không chỉ các 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(...)
và nhân số đó với số phần tử bạn muốn sao chép, nhưng sizeof không thể được sử dụng với một loại chung. Có thể - đối với một số loại - để sử dụng Marshal.SizeOf(typeof(T))
, nhưng bạn sẽ gặp lỗi thời gian chạy với một số loại nhất định (ví dụ: chuỗi). Ai đó có kiến thức kỹ lưỡng hơn về hoạt động bên trong của các loại CLR sẽ có thể chỉ ra tất cả các bẫy có thể có ở đây. Đủ để nói rằng viết một phương pháp nối mảng chung [sử dụng BlockCopy] không phải là chuyện nhỏ.
/// <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();
Tất cả những gì bạn cần để vượt qua danh sách Mảng Byte và hàm này sẽ trả về cho bạn Mảng Byte (Sáp nhập). Đây là giải pháp tốt nhất tôi nghĩ :).
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 là câu trả lời đúng, nhưng vì một số lý do, một thứ được kiểm soát sẽ nhận được nhiều phiếu bầu nhất. Nếu bạn thích câu trả lời đó, có lẽ bạn sẽ thích giải pháp tổng quát hơn này hơn nữa:
IEnumerable<byte> Combine(params byte[][] arrays)
{
foreach (byte[] a in arrays)
foreach (byte b in a)
yield return b;
}
Điều này sẽ cho phép bạn làm những việc như:
byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();