2バイト配列を連結する簡単な方法


249

2つのbyte配列を連結する簡単な方法は何ですか?

いう、

byte a[];
byte b[];

2つのbyte配列を連結して別のbyte配列に格納するにはどうすればよいですか?


3
注Apacheのコモンズ、Googleのグアバ、ことをしてくださいSystem.arrayCopyByteBufferと-ので、効率的ではなく読めませんが- ByteArrayOutputStreamすべてカバーされています。ここでは、7つ以上の回答が用意されています。これ以上の重複を投稿しないでください。
Maarten Bodewes

回答:


317

最も簡単です:

byte[] c = new byte[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);

377

これを行う最もエレガントな方法は、を使用することByteArrayOutputStreamです。

byte a[];
byte b[];

ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
outputStream.write( a );
outputStream.write( b );

byte c[] = outputStream.toByteArray( );

61
@vipwこれがエレガントな理由は、後で3番目の配列を連結したい場合は、行を追加するだけoutputStream.write( c );です。結果のバイト配列を作成する行に戻って編集する必要はありません。また、arraycopyメソッドを使用する場合とは異なり、配列の並べ替えは簡単です。
ウェインウロダ

2
さらに、これは2バイト以上の配列を扱う場合にはるかに簡単です。
gardarh 2013

3
CPUとメモリを浪費しているかどうかは、操作を実行する頻度によって異なります。1秒あたり10億回の場合は、最適化してください。それ以外の場合は、可読性と保守性が最も重要な考慮事項です。
vikingsteve 2013

5
メモリの消費やパフォーマンスが問題になるa.length + b.length場合は、ByteArrayOutputStreamコンストラクタの引数として使用してください。このメソッドは、すべてのバイトを新しい配列にコピーしてc[]!このByteBuffer方法は、メモリを浪費しない、近い候補と考えてください。
Maarten Bodewes 2014年

これは単なるコードスニペットであるため、これを高く評価することはできません。私が気にかけている部分である、基本的な部分の説明はここにはありません(そして、私はほとんどの人がそうすると思います)。System#arrayCopy(Object、int、Object、int、int)とByteArrayOutputStream#put(byte [])のパフォーマンスを比較し、両方のオプションに最適なシナリオを詳しく説明した場合は、喜んでこれを喜んでお伝えします。また、そうは言っても、別の解決策であるため、答えにはarrayCopyも含める必要があります。
searchengine27 2015

66

ここで使用して素敵なソリューションですグアバさんはcom.google.common.primitives.Bytes

byte[] c = Bytes.concat(a, b);

このメソッドの優れた点は、varargsシグニチャーがあることです。

public static byte[] concat(byte[]... arrays)

つまり、1回のメソッド呼び出しで任意の数の配列を連結できます。


30

別の可能性はを使用することjava.nio.ByteBufferです。

何かのようなもの

ByteBuffer bb = ByteBuffer.allocate(a.length + b.length + c.length);
bb.put(a);
bb.put(b);
bb.put(c);
byte[] result = bb.array();

// or using method chaining:

byte[] result = ByteBuffer
        .allocate(a.length + b.length + c.length)
        .put(a).put(b).put(c)
        .array();

配列は最初は適切なサイズになっている必要があるため、割り当てラインが必要です(array()オフセット、位置、制限を考慮せずに、単にバッキング配列を返すため)。


3
@click_whir申し訳ありませんが、ReadTheDocs。ByteBuffer.allocate(int)インスタンス化さjava.nio.HeapByteBufferれたのサブクラスを返す静的メソッドですByteBuffer。方法-と、他の抽象ネス-の世話をしています。.put().compact()
kalefranz 2014年

@kalefranz compact()正しくないため、行を削除しました。
Maarten Bodewes 2014

1
ByteBufferのarray()メソッドを使用する場合は注意してください。何をしているのかが完全にわかっていて、保守性が問題でない場合を除き、バイトバッファの0番目の位置が常にバイト配列のインデックス0に対応するという保証はありません。こちらをご覧ください。行のbb.flip(); bb.get(result);代わりに発行することでこれを解決しbyte[] result = bb.array();ます。
DarqueSandu

1
@DarqueSandu 一般的には良いアドバイスですが、allocateメソッドを注意深く読むと、次のことがわかります。 。それはバッキング配列を持ち、その配列オフセットはゼロになります。」したがって、が内部的に割り当てられているこの特定のコードでByteBufferは、問題はありません。
Maarten Bodewes

13

別の方法は、ユーティリティ関数を使用することです(必要に応じて、これを汎用ユーティリティクラスの静的メソッドにすることができます)。

byte[] concat(byte[]...arrays)
{
    // Determine the length of the result array
    int totalLength = 0;
    for (int i = 0; i < arrays.length; i++)
    {
        totalLength += arrays[i].length;
    }

    // create the result array
    byte[] result = new byte[totalLength];

    // copy the source arrays into the result array
    int currentIndex = 0;
    for (int i = 0; i < arrays.length; i++)
    {
        System.arraycopy(arrays[i], 0, result, currentIndex, arrays[i].length);
        currentIndex += arrays[i].length;
    }

    return result;
}

次のように呼び出します。

byte[] a;
byte[] b;
byte[] result = concat(a, b);

3、4、5配列などの連結にも使用できます。

この方法でこれを行うと、読み取りと保守が非常に簡単な、高速のarraycopyコードの利点が得られます。


11
byte[] result = new byte[a.length + b.length];
// copy a to result
System.arraycopy(a, 0, result, 0, a.length);
// copy b to result
System.arraycopy(b, 0, result, a.length, b.length);

承認された回答と同じ回答です。申し訳ありませんが、5分遅れます。
Maarten Bodewes

11

ByteBuffer@kalefranzのようにしたい場合は、次のように、2つbyte[](またはそれ以上)を1行で連結することが常に可能です。

byte[] c = ByteBuffer.allocate(a.length+b.length).put(a).put(b).array();

同じような答えこれが、1年以上遅れて。メソッドチェーンを使用しますが、既存の答えに入れたほうがよいでしょう。
Maarten Bodewes

11

Apache Commons Langのようなクリーンコード用のサードパーティライブラリを使用して、次のように使用できます。

byte[] bytes = ArrayUtils.addAll(a, b);

1
私が試したArrayUtils.addAll(a, b)byte[] c = Bytes.concat(a, b)、後者は高速です。
CarlosAndrésGarcía16年

多分。Guavaライブラリは知らないので、もしそうなら、それを使用する方が良いです。非常に大きな配列をチェックしましたか?
Tomasz Przybylski 2016

1
テストを行ったとき、Firts配列は68要素の長さで、2番目の8790688の長さでした。
CarlosAndrésGarcía16年

5

2つまたは複数のアレイの場合、このシンプルでクリーンなユーティリティメソッドを使用できます。

/**
 * Append the given byte arrays to one big array
 *
 * @param arrays The arrays to append
 * @return The complete array containing the appended data
 */
public static final byte[] append(final byte[]... arrays) {
    final ByteArrayOutputStream out = new ByteArrayOutputStream();
    if (arrays != null) {
        for (final byte[] array : arrays) {
            if (array != null) {
                out.write(array, 0, array.length);
            }
        }
    }
    return out.toByteArray();
}

1
これはメモリを浪費します。この方法は、2つの小さな配列では問題ありませんが、それ以上の配列ではガベージコレクターに負荷がかかります。
Maarten Bodewes

1

2つのPDFバイト配列をマージする

PDFを含む2つのバイト配列をマージする場合、このロジックは機能しません。ApacheのPDFboxなどのサードパーティツールを使用する必要があります。

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
mergePdf.addSource(new ByteArrayInputStream(a));
mergePdf.addSource(new ByteArrayInputStream(b));
mergePdf.setDestinationStream(byteArrayOutputStream);
mergePdf.mergeDocuments();
c = byteArrayOutputStream.toByteArray();

この質問には少し外れたトピックですが、まさに私が探していたものです。
amos

1

配列のサイズを台無しにしたくない場合は、文字列連結の魔法を使用してください:

byte[] c = (new String(a, "l1") + new String(b, "l1")).getBytes("l1");

または、コードのどこかに定義します

// concatenation charset
static final java.nio.charset.Charset cch = java.nio.charset.StandardCharsets.ISO_8859_1;

そして使う

byte[] c = (new String(a, cch) + new String(b, cch)).getBytes(cch);

もちろん、これは+加算演算子を使用した3つ以上の文字列連結でも機能します。


"l1"ISO_8859_1はどちらも、各文字を1バイトとしてエンコードする西ラテン1文字セットを示します。マルチバイト変換は実行されないため、文字列内の文字はバイトと同じ値になります(char符号なしのように常に正の値として解釈されることを除いて)。したがって、少なくともOracle提供のランタイムでは、バイトは正しく「デコード」されてから、再度「エンコード」されます。

文字列はバイト配列をかなり拡張し、追加のメモリが必要になることに注意してください。文字列もインターンされる可能性があるため、簡単には削除できません。文字列も不変なので、文字列内の値は破棄できません。したがって、機密性の高い配列をこの方法で連結したり、大きなバイト配列にこのメソッドを使用したりしないでください。この配列の連結方法は一般的なソリューションではないため、実行していることを明確に示すことも必要になります。


@MaartenBodewes「l1」(ISO 8859-1の単なる別名)が不明な場合は、「確実に」という言葉を使用しないでください。どの特定のバイト値が消去されますか?メモリ使用量に関しては、ほとんどのメモリ効率の良い配列ではなく、2バイト配列を連結する簡単な方法が問題でした。
John McClane

1
私はいくつかの警告を書き留め、いくつかのテストを行いました。Latin 1およびOracle提供のランタイム(11)の場合、これは機能するようです。そのため、追加情報を提供し、コメントと反対投票を削除しました。問題がなければ、ロールバックしてください。
Maarten Bodewes

0

これが私のやり方です!

public static byte[] concatByteArrays(byte[]... inputs) {
    int i = inputs.length - 1, len = 0;
    for (; i >= 0; i--) {
        len += inputs[i].length;
    }
    byte[] r = new byte[len];
    for (i = inputs.length - 1; i >= 0; i--) {
        System.arraycopy(inputs[i], 0, r, len -= inputs[i].length, inputs[i].length);
    }
    return r;
}

特徴

  • varargs(...)を使用して、任意のバイト数で呼び出します[]。
  • System.arraycopy()高速動作を保証するために、マシン固有のネイティブコードで実装された使用。
  • 必要な正確なサイズで新しいbyte []を作成します。
  • および変数をint再利用して、割り当てる変数を少なくilenます。
  • 定数との高速比較。

覚えておいてください

これを行うより良い方法は、@ Jonathanコードをコピーすることです。このデータ型が別の関数に渡されるとJavaが新しい変数を作成するため、問題はネイティブ変数配列に起因します。


1
いいえ、それがウェインのやり方です。あなたは5年遅れています。
Maarten Bodewes

@MaartenBodewesおかげで、今日のコーディングを実行するためにあなたのコメントを使用しますが、今はより異なり、パフォーマンスが向上しています。
Daniel

1
配列のサイズがランタイムでも変化しないことを考えると、それがあまり重要であるかどうかはわかりませんが、少なくとも他のソリューションとは異なります。
Maarten Bodewes
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.