web-dev-qa-db-ja.com

C#で2つ以上のバイト配列を追加する

C#で2つのバイト配列を追加する最良の(以下を参照)方法はありますか?

完全に制御しているふりをして、最後に2番目のバイト配列を保持するのに十分な大きさの最初のバイト配列を作成し、 Array.CopyTo 関数を使用できます。または、個々のバイトをループして割り当てを行うことができます。

もっと良い方法はありますか?バイト配列を文字列に変換し、それらを結合して元に戻すことは、上記のいずれの方法よりも優れているとは思いません。

ベスト/ベター(順):

  1. 最速
  2. 最小RAM消費

制約は、.NET 2.0フレームワークで作業する必要があるということです。

推奨される2つの選択肢は、MemoryStreamとBlockCopyです。 10,000,000ループの簡単な速度テストを3回実行しましたが、次の結果が得られました。

ミリ秒単位での10,000,000ループの3回の実行の平均:

  • BlockCopy Time:1154、13ミリ秒の範囲
  • MemoryStream GetBuffer Time:1470、範囲は14ミリ秒
  • MemoryStream ToArray Time:1895、3ミリ秒の範囲
  • CopyTo Time:2079、範囲は19ミリ秒
  • バイト単位の時間:2203、範囲は10ミリ秒

List <byte>の結果 AddRange 1000万ループ以上:List <byte>時間:16694

相対RAM消費(1はベースライン、高いほど悪い):

  • バイトごと:1
  • BlockCopy:1
  • コピー先:1
  • MemoryStream GetBuffer:2.3
  • MemoryStream ToArray:3.3
  • リスト<バイト>:4.2

このテストは、一般に、多くのバイトコピー[私()を実行している場合を除き、バイトコピーを見ることは注目に値しません[例えば1,000万回実行すると、最大で1.1秒の差が生じます。

30
torial

欲しい BlockCopy

このブログ投稿 によると、Array.CopyToよりも高速です。

24
dss539

MemoryStreamを使用したアプローチも使用できます。 b1とb2が2バイトの配列であると仮定すると、次の方法でMemoryStreamを使用して、新しいb3を取得できます。

var s = new MemoryStream();
s.Write(b1, 0, b1.Length);
s.Write(b2, 0, b2.Length);
var b3 = s.ToArray();

これはLINQなしで機能するはずで、実際にはかなり高速です。

14
flq

新しい MemoryStream を作成し、マージされたバッファーとまったく同じサイズのバッファーをコンストラクターに渡します。個々の配列を作成し、最後にバッファーを使用します。

byte[] deadBeef = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF};
byte[] baadF00d = new byte[] { 0xBA, 0xAD, 0xF0, 0x0D};
int newSize = deadBeef.Length + baadF00d.Length;
var ms = new MemoryStream(new byte[newSize], 0, newSize, true, true);
ms.Write(deadBeef, 0, deadBeef.Length);
ms.Write(baadF00d, 0, baadF00d.Length);
byte[] merged = ms.GetBuffer();

.NETの低レベルI/O関数の多くは、バイト配列とオフセットを取ります。これは、不必要なコピーを防ぐために行われました。パフォーマンスが重要な場合は、マージされた配列が本当に必要であることを確認してください。そうでない場合は、バッファーとオフセットを使用してください。

11
Jeff Moser

別のオプションは、速度とメモリ消費の点でどのように機能するかを確認するためにテストしていませんが、LINQアプローチは次のようになります。

byte[] combined = bytesOne.Concat(bytesTwo).Concat(bytesThree).ToArray();

...ここで、bytesOne、bytesTwo、およびbytesThreeはバイト配列です。 Concatは遅延実行を使用するため、これは中間配列を作成してはならず、最後にマージされた最終的な配列を構築するまで元の配列を複製してはなりません。

編集: LINQBridge を使用すると、2.0フレームワークでLINQ-to-Objects(これは一例です)を使用できます。あなたがこれに依存したくない場合、私は理解していますが、それはオプションです。

6
Joel Mueller

サイズが時々変化する配列がある場合は、最初に_List<T>_を使用する方が良いでしょう。次に、リストのAddRange()メソッドを呼び出すだけです。

それ以外の場合、Array.Copy()またはArray.CopyTo()は、他のどのようなものよりも優れています。

3
Joel Coehoorn

出力を実際にバイト配列にする必要がありますか?

そうでない場合は、自分で「スマートカーソル」を作成できます(これはLINQの機能と似ています)。最初の配列を最初に反復し、中断することなく2番目の配列で続行するカスタムIEnumerator <byte>を作成します。

これは2.0フレームワークで高速に動作し(配列の結合には実質的にコストがかからないため)、配列がすでに消費している以上のRAMは使用しません。

2
Arjan Einbu

配列の代わりにListまたはArrayListを使用することを教えましたか?これらのタイプでは、InsertRangeを介して拡大または縮小して追加できます。

2
AndrewB

最初の配列を2番目の配列を含めるのに十分な大きさにし、Array.CopyToを使用する最初のオプションは、各アイテムを手動で繰り返して割り当てを行うのとほぼ同じです。 Array.CopyTo()は、より簡潔にします。

文字列への変換と配列への変換は、上記とは対照的にひどく遅くなります。そして、おそらくより多くのメモリを使用します。

0
Nate