ฉันมีอาร์เรย์ 3 ไบต์ใน C # ที่ฉันต้องรวมเข้าเป็นหนึ่งเดียว อะไรจะเป็นวิธีที่มีประสิทธิภาพที่สุดในการทำภารกิจนี้ให้สำเร็จ
ฉันมีอาร์เรย์ 3 ไบต์ใน C # ที่ฉันต้องรวมเข้าเป็นหนึ่งเดียว อะไรจะเป็นวิธีที่มีประสิทธิภาพที่สุดในการทำภารกิจนี้ให้สำเร็จ
คำตอบ:
สำหรับรูปแบบดั้งเดิม (รวม bytes) ใช้แทนSystem.Buffer.BlockCopy
System.Array.Copy
มันเร็วกว่า
ฉันหมดเวลาแต่ละวิธีที่แนะนำในการวนซ้ำ 1 ล้านครั้งโดยใช้ 3 อาร์เรย์ 10 ไบต์ นี่คือผลลัพธ์:
System.Array.Copy
- 0.2187556 วินาทีSystem.Buffer.BlockCopy
- 0.1406286 วินาทีฉันเพิ่มขนาดของแต่ละอาร์เรย์เป็น 100 องค์ประกอบและทำการทดสอบอีกครั้ง:
System.Array.Copy
- 0.2812554 วินาทีSystem.Buffer.BlockCopy
- 0.2500048 วินาทีฉันเพิ่มขนาดของแต่ละอาร์เรย์เป็น 1,000 อิลิเมนต์และทำการทดสอบอีกครั้ง:
System.Array.Copy
- 1.0781457 วินาทีSystem.Buffer.BlockCopy
- 1.0156445 วินาทีในที่สุดฉันเพิ่มขนาดของแต่ละอาร์เรย์เป็น 1 ล้านองค์ประกอบและเรียกใช้การทดสอบอีกครั้งเรียกใช้แต่ละวงเพียง 4000 ครั้ง:
System.Array.Copy
- 13.4533833 วินาทีSystem.Buffer.BlockCopy
- 13.1096267 วินาทีดังนั้นหากคุณต้องการอาร์เรย์ไบต์ใหม่ให้ใช้
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);
แต่ถ้าคุณสามารถใช้IEnumerable<byte>
, แน่นอนต้องการของ LINQ Concat <> วิธี มันช้ากว่าผู้ให้บริการ C # เพียงเล็กน้อยเท่านั้น แต่มีความกระชับและสง่างามกว่า
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
หากคุณมีอาร์เรย์จำนวนเท่าใดและใช้. NET 3.5 คุณสามารถSystem.Buffer.BlockCopy
แก้ปัญหาแบบทั่วไปได้มากกว่านี้:
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;
}
* หมายเหตุ: บล็อกด้านบนกำหนดให้คุณเพิ่มเนมสเปซต่อไปนี้ที่ด้านบนเพื่อให้สามารถใช้งานได้
using System.Linq;
สำหรับประเด็นของ Jon Skeet เกี่ยวกับการวนซ้ำของโครงสร้างข้อมูลที่ตามมา (ไบต์อาร์เรย์กับ IEnumerable <byte>) ฉันทำการทดสอบการจับเวลาครั้งสุดท้ายอีกครั้ง (1 ล้านองค์ประกอบ 4,000 การวนซ้ำ) เพิ่มการวนซ้ำที่เต็มไปด้วยอาร์เรย์ทั้งหมด ผ่าน:
System.Array.Copy
- 78.20550510 วินาทีSystem.Buffer.BlockCopy
- 77.89261900 วินาทีประเด็นก็คือมันสำคัญมากที่จะเข้าใจประสิทธิภาพของการสร้างและการใช้โครงสร้างข้อมูลที่เกิดขึ้น เพียงแค่มุ่งเน้นไปที่ประสิทธิภาพของการสร้างอาจมองข้ามความไร้ประสิทธิภาพที่เกี่ยวข้องกับการใช้งาน รุ่งโรจน์จอน
คำตอบหลายข้อดูเหมือนจะทำให้ฉันเพิกเฉยต่อข้อกำหนดที่ระบุไว้:
ทั้งสองร่วมกันออกกฎเรียงลำดับ LINQ ไบต์ - สิ่งที่yield
จะทำให้เป็นไปไม่ได้ที่จะได้รับขนาดสุดท้ายโดยไม่ต้องวนซ้ำผ่านลำดับทั้งหมด
หากสิ่งเหล่านั้นไม่ใช่ข้อกำหนดที่แท้จริงของหลักสูตร LINQ อาจเป็นทางออกที่ดีอย่างสมบูรณ์ (หรือIList<T>
การนำไปใช้) อย่างไรก็ตามฉันจะสมมติว่า Superdumbell รู้ว่าเขาต้องการอะไร
(แก้ไข: ฉันได้เพียงแค่มีความคิดอื่นมีความแตกต่างความหมายใหญ่ระหว่างทำสำเนาของอาร์เรย์และอ่านได้อย่างเฉื่อยชาพิจารณาสิ่งที่เกิดขึ้นถ้าคุณเปลี่ยนข้อมูลในหนึ่งใน "ต้นฉบับ" อาร์เรย์หลังจากเรียก.. Combine
(หรืออะไรก็ตาม ) วิธีการ แต่ก่อนที่จะใช้ผลลัพธ์ - ด้วยการประเมินผลที่ขี้เกียจการเปลี่ยนแปลงนั้นจะสามารถมองเห็นได้โดยมีการคัดลอกทันทีมันจะไม่สถานการณ์ต่าง ๆ จะเรียกใช้พฤติกรรมที่แตกต่างกัน - เป็นสิ่งที่ต้องระวัง)
นี่คือวิธีการที่ฉันเสนอ - ซึ่งคล้ายกับวิธีที่มีอยู่ในคำตอบอื่น ๆ อย่างแน่นอน :)
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;
}
แน่นอนว่ารุ่น "params" นั้นจำเป็นต้องสร้างอาร์เรย์ของอาร์เรย์ไบต์ก่อนซึ่งจะเป็นการเพิ่มประสิทธิภาพที่ต่ำลง
ฉันใช้ตัวอย่าง LINQ ของ Matt อีกหนึ่งขั้นตอนเพื่อความสะอาดของโค้ด:
byte[] rv = a1.Concat(a2).Concat(a3).ToArray();
ในกรณีของฉันอาร์เรย์มีขนาดเล็กดังนั้นฉันจึงไม่กังวลเกี่ยวกับประสิทธิภาพ
หากคุณต้องการอาร์เรย์ไบต์ใหม่ให้ใช้สิ่งต่อไปนี้:
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;
}
หรือถ้าคุณต้องการ IEnumerable เพียงอันเดียวให้ลองใช้ตัวดำเนินการผลตอบแทน 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;
}
ที่จริงฉันเจอปัญหาบางอย่างกับการใช้ Concat ... (กับอาร์เรย์ใน 10 ล้านมันล้มเหลวจริงๆ)
ฉันพบว่าสิ่งต่อไปนี้นั้นง่ายเรียบง่ายและทำงานได้ดีพอโดยไม่ต้องทำอะไรเลยและมันใช้ได้กับอาร์เรย์จำนวนเท่าใดก็ได้ (ไม่ใช่แค่สาม) (ใช้ LINQ):
public static byte[] ConcatByteArrays(params byte[][] arrays)
{
return arrays.SelectMany(x => x).ToArray();
}
คลาส memorystream ทำงานนี้ค่อนข้างดีสำหรับฉัน ฉันไม่สามารถทำให้คลาสบัฟเฟอร์ทำงานได้เร็วเท่ากับ 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
) แต่ - ไม่ใช่การเป็นผู้เชี่ยวชาญในอวัยวะภายในของ CLR - ฉันไม่สามารถพูดได้ว่าคุณอาจได้รับข้อยกเว้นใน structs บางอย่างเช่นกัน (เช่นหากมีเขตข้อมูลประเภทการอ้างอิง)
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();
}
}
สามารถใช้ข้อมูลทั่วไปเพื่อรวมอาร์เรย์ รหัสต่อไปนี้สามารถขยายได้อย่างง่ายดายถึงสามอาร์เรย์ วิธีนี้คุณไม่จำเป็นต้องทำสำเนารหัสสำหรับอาร์เรย์ประเภทอื่น คำตอบข้างต้นบางคำดูเหมือนซับซ้อนเกินไปสำหรับฉัน
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;
}
ต่อไปนี้เป็นคำอธิบายทั่วไปของคำตอบที่ @Jon Skeet มันเป็นพื้นเดียวกันเท่านั้นที่สามารถใช้ได้กับอาร์เรย์ทุกประเภทไม่เพียงไบต์:
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(...)
และคูณด้วยจำนวนองค์ประกอบที่คุณต้องการคัดลอก แต่ขนาดขององค์ประกอบไม่สามารถใช้กับประเภททั่วไปได้ เป็นไปได้ - สำหรับบางประเภท - ที่จะใช้Marshal.SizeOf(typeof(T))
แต่คุณจะได้รับข้อผิดพลาดรันไทม์ที่มีบางประเภท (เช่นสตริง) คนที่มีความรู้อย่างละเอียดมากขึ้นเกี่ยวกับการทำงานภายในของประเภท CLR จะสามารถระบุกับดักที่เป็นไปได้ทั้งหมดที่นี่ พอจะกล่าวได้ว่าการเขียนวิธีเรียงต่อกันแบบทั่วไป [ใช้ BlockCopy] นั้นไม่สำคัญ
/// <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();
ทั้งหมดที่คุณต้องผ่านรายการ Byte Arrays และฟังก์ชั่นนี้จะคืน Array of Bytes ให้คุณ นี่เป็นทางออกที่ดีที่สุดที่ฉันคิดว่า :)
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 เป็นคำตอบที่ถูกต้อง แต่ด้วยเหตุผลบางอย่างสิ่งที่ทำด้วยมือได้รับคะแนนเสียงมากที่สุด หากคุณชอบคำตอบนั้นบางทีคุณอาจต้องการคำตอบทั่วไปนี้มากยิ่งขึ้น:
IEnumerable<byte> Combine(params byte[][] arrays)
{
foreach (byte[] a in arrays)
foreach (byte b in a)
yield return b;
}
ซึ่งจะช่วยให้คุณทำสิ่งต่าง ๆ เช่น:
byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();