ChatGPT解决这个技术问题 Extra ChatGPT

Easy way to concatenate two byte arrays

What is the easy way to concatenate two byte arrays?

Say,

byte a[];
byte b[];

How do I concatenate two byte arrays and store it in another byte array?

Note please that Apache Commons, Google's Guava, System.arrayCopy, ByteBuffer and the - not so efficient but readable - ByteArrayOutputStream have all been covered. We've got more than 7 dupes of the answers given here. Please don't post any more dupes.

A
AskNilesh

The most elegant way to do this is with a ByteArrayOutputStream.

byte a[];
byte b[];

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

byte c[] = outputStream.toByteArray( );

@vipw The reason why this is elegant is because if/when you wish to concatenate a third array later, you simply add the line outputStream.write( c ); - you don't have to go back and edit the line where you create the result byte array. Also, re-ordering the arrays is simple, unlike using the arraycopy method.
Additionally this is far easier when working with more than just 2 byte arrays.
Whether it's wasting cpu and memory depends on how often you do the operation. If it's a billion times a second - sure, optimize it. Otherwise, readability and maintainability might be the winning considerations.
If memory consumption and/or performance is a concern, be sure to use a.length + b.length as argument for the ByteArrayOutputStream constructor. Note that this method still will copy all bytes to a new array to assign to c[]! Consider the ByteBuffer method a close contender, that does not waste memory.
I can't really give this a thumbs up because this is just a code snippet. There is no explanation of the underlying pieces here, which is the part I care about (and I think most people would). I would gladly give this a thumbs up if there was a performance comparison between System#arrayCopy(Object, int, Object, int, int) and ByteArrayOutputStream#put(byte[]), and detailed which scenario is best for both options. Also, that being said, the answer should also include arrayCopy since that is another solution.
J
Jonathan

Most straightforward:

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);

worked like charm
Z
Zoltán

Here's a nice solution using Guava's com.google.common.primitives.Bytes:

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

The great thing about this method is that it has a varargs signature:

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

which means that you can concatenate an arbitrary number of arrays in a single method call.


This is the simplest way. BTW, no difference if you are using Groovy.
P
Pavel Repin

Another possibility is using java.nio.ByteBuffer.

Something like

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();

Note that the array must be appropriately sized to start with, so the allocation line is required (as array() simply returns the backing array, without taking the offset, position or limit into account).


@click_whir Sorry man, but ReadTheDocs. ByteBuffer.allocate(int) is a static method that returns an instantiated java.nio.HeapByteBuffer, a subclass of ByteBuffer. The .put() and .compact() methods--and any other abstract-ness--is taken care of.
@kalefranz Removed compact() line as it is incorrect.
Take care in using the ByteBuffer's array() method - unless you absolutely know what you are doing and maintainability is not an issue, there are no guarantees that the zeroeth position in the bytebuffer always corresponds to index 0 of the byte array. See here. I solve this by issuing bb.flip(); bb.get(result); in stead of the byte[] result = bb.array(); line.
@DarqueSandu Although that's good advice in general, careful reading of the allocate method reveals the following: " The new buffer's position will be zero, its limit will be its capacity, its mark will be undefined, and each of its elements will be initialized to zero. It will have a backing array, and its array offset will be zero." So for this particular piece of code, where the ByteBuffer is allocated internally, it's not an issue.
W
Wayne Uroda

Another way is to use a utility function (you could make this a static method of a generic utility class if you like):

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;
}

Invoke like so:

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

It will also work for concatenating 3, 4, 5 arrays, etc.

Doing it this way gives you the advantage of fast arraycopy code which is also very easy to read and maintain.


T
Tomasz Przybylski

You can use third party libraries for Clean Code like Apache Commons Lang and use it like:

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

I tried ArrayUtils.addAll(a, b) and byte[] c = Bytes.concat(a, b), but the latter is faster.
Maybe. I don't know Guava library so if it is, it's better to use it. Did you check it for very large arrays?
When I did the test, The Firts array was 68 elements length y the second 8790688 length.
J
James.Xu
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);

Same answer as the accepted one and sorry, 5 minutes late.
Z
ZEuS

If you prefer ByteBuffer like @kalefranz, there is always the posibility to concatenate two byte[] (or even more) in one line, like this:

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

Same answer as this one but more than 1 year late. Uses method chaining, but that would be better put into the existing answer.
J
Jeroen Meulemeester

For two or multiple arrays, this simple and clean utility method can be used:

/**
 * 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();
}

This wastes memory. The method would be OK for two smaller arrays, but it certainly will tax the garbage collector for more arrays.
C
Community

Merge two PDF byte arrays

If you are merging two byte arrays which contain PDF, this logic will not work. We need to use a third-party tool like PDFbox from Apache:

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

a bit off topic to this question, but is exactly what I was looking for.
M
Maarten Bodewes

If you don't want to mess with arrays' sizes, just use the magic of string concatenation:

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

Or define somewhere in your code

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

and use

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

This, of course, also works with more than two string concatenations using the + addition operator.

Both "l1" and ISO_8859_1 indicate the Western Latin 1 character set that encodes each character as a single byte. As no multi-byte translations are performed the characters in the string will have the same values as the bytes (except that they will always be interpreted as positive values, as char is unsigned). At least for the Oracle provided runtime, any byte will therefore be correctly "decoded" and then "encoded" again.

Beware that strings do expand the byte array considerately, requiring additional memory. Strings may also be interned and will therefore not easy be removed. Strings are also immutable, so the values inside them cannot be destroyed. You should therefore not concatenate sensitive arrays this way nor should you use this method for larger byte arrays. Giving a clear indication of what you are doing would also be required, as this method of array concatenation is not a common solution.


@MaartenBodewes If you are not sure about "l1" (which is just an alias for ISO 8859-1), don't use the word "certainly". Which particular byte value will be wiped out? As for the memory usage, the question was about the easy way to concatenate two byte arrays, not about most memory efficient one.
I've put down some warnings and did some testing. For Latin 1 and the Oracle provided runtime (11) this does seem to work. So I provided extra info and removed my comment and downvote. I hope that's OK for you, otherwise please roll back.
D
Daniel De León

This is my way to do it!

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;
}

Features:

Use varargs (...) to be called with any number of byte[].

Use System.arraycopy() that is implemented with machine specific native code, to ensure high speed operation.

Create a new byte[] with the exact size that is need it.

Allocate little less int variables by reusing the i and len variables.

Faster comparison with constants.

Keep in mind:

The better way to do this, is by copy the @Jonathan code. The issue comes from the native variable arrays, because Java create new variables when this data type is passed to another function.


No, that's Wayne's way to do it, you're 5 years late.
@MaartenBodewes Thanks to you, I use your comment to exercise coding today, now is more different and with better performance.
I'm not sure it will matter too much, seeing that array sizes do not change in the runtime either, but it is now different at least from the other solution.
m
madx

If you are already loading Guava library you can use static method concat(byte[]... arrays) from com.google.common.primitives.Bytes:

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

Here is the standalone source of the concat(byte[]... arrays) method by "Kevin Bourrillion":

public static byte[] concat(byte[]... arrays) {
    int length = 0;
    for (byte[] array : arrays) {
        length += array.length;
    }
    byte[] result = new byte[length];
    int pos = 0;
    for (byte[] array : arrays) {
        System.arraycopy(array, 0, result, pos, array.length);
        pos += array.length;
    }
    return result;
}