Ich habe 3-Byte-Arrays in C #, die ich zu einem kombinieren muss. Was wäre die effizienteste Methode, um diese Aufgabe zu erledigen?
Ich habe 3-Byte-Arrays in C #, die ich zu einem kombinieren muss. Was wäre die effizienteste Methode, um diese Aufgabe zu erledigen?
Antworten:
Verwenden Sie für primitive Typen (einschließlich Bytes) System.Buffer.BlockCopyanstelle von System.Array.Copy. Es ist schneller.
Ich habe jede der vorgeschlagenen Methoden in einer Schleife zeitlich festgelegt, die 1 Million Mal ausgeführt wurde, wobei 3 Arrays mit jeweils 10 Bytes verwendet wurden. Hier sind die Ergebnisse:
System.Array.Copy - 0,2187556 SekundenSystem.Buffer.BlockCopy - 0.1406286 SekundenIch habe die Größe jedes Arrays auf 100 Elemente erhöht und den Test erneut ausgeführt:
System.Array.Copy - 0,2812554 SekundenSystem.Buffer.BlockCopy - 0,2500048 SekundenIch habe die Größe jedes Arrays auf 1000 Elemente erhöht und den Test erneut ausgeführt:
System.Array.Copy - 1.0781457 SekundenSystem.Buffer.BlockCopy - 1.0156445 SekundenSchließlich habe ich die Größe jedes Arrays auf 1 Million Elemente erhöht und den Test erneut ausgeführt, wobei jede Schleife nur 4000 Mal ausgeführt wurde:
System.Array.Copy - 13.4533833 SekundenSystem.Buffer.BlockCopy - 13.1096267 SekundenWenn Sie also ein neues Byte-Array benötigen, verwenden Sie
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);
Wenn Sie jedoch eine verwenden können IEnumerable<byte>, bevorzugen Sie definitiv die Concat <> -Methode von LINQ. Es ist nur geringfügig langsamer als der C # -Ertragsoperator, aber prägnanter und eleganter.
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
Wenn Sie eine beliebige Anzahl von Arrays haben und .NET 3.5 verwenden, können Sie die System.Buffer.BlockCopyLösung wie folgt allgemeiner gestalten:
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;
}
* Hinweis: Für den obigen Block müssen Sie oben den folgenden Namespace hinzufügen, damit er funktioniert.
using System.Linq;
Zu Jon Skeets Punkt bezüglich der Iteration der nachfolgenden Datenstrukturen (Byte-Array vs. IEnumerable <Byte>) habe ich den letzten Timing-Test (1 Million Elemente, 4000 Iterationen) erneut ausgeführt und eine Schleife hinzugefügt, die mit jedem über das gesamte Array iteriert bestehen:
System.Array.Copy - 78.20550510 SekundenSystem.Buffer.BlockCopy - 77.89261900 SekundenDer Punkt ist, dass es SEHR wichtig ist, die Effizienz sowohl der Erstellung als auch der Verwendung der resultierenden Datenstruktur zu verstehen . Wenn Sie sich nur auf die Effizienz der Erstellung konzentrieren, wird möglicherweise die mit der Verwendung verbundene Ineffizienz übersehen. Ein großes Lob, Jon.
Viele der Antworten scheinen mir die angegebenen Anforderungen zu ignorieren:
Diese beiden zusammen schließen eine LINQ-Folge von Bytes aus - alles, yieldwas dazu gehört, macht es unmöglich, die endgültige Größe zu erhalten, ohne die gesamte Folge zu durchlaufen.
Wenn dies natürlich nicht die tatsächlichen Anforderungen sind, könnte LINQ eine perfekte Lösung (oder die IList<T>Implementierung) sein. Ich gehe jedoch davon aus, dass Superdumbell weiß, was er will.
(BEARBEITEN: Ich habe gerade einen anderen Gedanken gehabt. Es gibt einen großen semantischen Unterschied zwischen dem Erstellen einer Kopie der Arrays und dem trägen Lesen. Überlegen Sie, was passiert, wenn Sie die Daten in einem der "Quell" -Arrays nach dem Aufrufen des Combine(oder was auch immer) ändern ) Methode, aber bevor das Ergebnis verwendet wird - bei verzögerter Auswertung wird diese Änderung sichtbar. Bei einer sofortigen Kopie wird dies nicht der Fall sein. Unterschiedliche Situationen erfordern unterschiedliches Verhalten - nur etwas, das Sie beachten sollten.)
Hier sind meine vorgeschlagenen Methoden - die denen in einigen anderen Antworten sehr ähnlich sind, sicherlich :)
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;
}
Natürlich erfordert die "params" -Version, dass zuerst ein Array der Byte-Arrays erstellt wird, was zu zusätzlicher Ineffizienz führt.
Ich habe Matts LINQ-Beispiel für die Code-Sauberkeit noch einen Schritt weiter gebracht:
byte[] rv = a1.Concat(a2).Concat(a3).ToArray();
In meinem Fall sind die Arrays klein, daher mache ich mir keine Sorgen um die Leistung.
Wenn Sie einfach ein neues Byte-Array benötigen, verwenden Sie Folgendes:
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;
}
Wenn Sie nur eine einzige IEnumerable benötigen, können Sie alternativ den C # 2.0-Ertragsoperator verwenden:
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;
}
Ich bin tatsächlich auf einige Probleme bei der Verwendung von Concat gestoßen ... (bei Arrays im Wert von 10 Millionen ist es tatsächlich abgestürzt).
Ich fand das Folgende einfach, leicht und funktioniert gut genug, ohne auf mich zu stürzen, und es funktioniert für JEDE Anzahl von Arrays (nicht nur drei) (es verwendet LINQ):
public static byte[] ConcatByteArrays(params byte[][] arrays)
{
return arrays.SelectMany(x => x).ToArray();
}
Die Memorystream-Klasse macht diesen Job ziemlich gut für mich. Ich konnte die Pufferklasse nicht so schnell wie Memorystream laufen lassen.
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), aber da ich kein Experte für die Innereien der CLR bin, kann ich nicht sagen, ob Sie möglicherweise auch Ausnahmen für bestimmte Strukturen erhalten (zB wenn sie Referenztypfelder enthalten).
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();
}
}
Kann Generika verwenden, um Arrays zu kombinieren. Der folgende Code kann einfach auf drei Arrays erweitert werden. Auf diese Weise müssen Sie niemals Code für verschiedene Arten von Arrays duplizieren. Einige der oben genannten Antworten erscheinen mir zu komplex.
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;
}
Hier ist eine Verallgemeinerung der Antwort von @Jon Skeet. Es ist im Grunde das gleiche, nur ist es für jede Art von Array verwendbar, nicht nur für Bytes:
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(...)und multiplizieren diese mit der Anzahl der Elemente, die Sie kopieren möchten. Sizeof kann jedoch nicht mit einem generischen Typ verwendet werden. Bei einigen Typen ist die Verwendung möglich Marshal.SizeOf(typeof(T)), bei bestimmten Typen (z. B. Zeichenfolgen) treten jedoch Laufzeitfehler auf. Jemand, der das Innenleben von CLR-Typen genauer kennt, kann hier auf alle möglichen Fallen hinweisen. Es genügt zu sagen, dass das Schreiben einer generischen Array-Verkettungsmethode [mit BlockCopy] nicht trivial ist.
/// <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();
Alles, was Sie brauchen, um eine Liste von Byte-Arrays zu übergeben, und diese Funktion gibt Ihnen das Array von Bytes (zusammengeführt) zurück. Dies ist die beste Lösung, denke ich :).
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 ist die richtige Antwort, aber aus irgendeinem Grund erhält eine handgerollte Sache die meisten Stimmen. Wenn Ihnen diese Antwort gefällt, möchten Sie diese allgemeinere Lösung vielleicht noch mehr:
IEnumerable<byte> Combine(params byte[][] arrays)
{
foreach (byte[] a in arrays)
foreach (byte b in a)
yield return b;
}
was Sie Dinge tun lassen würde wie:
byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();