连接两个字节数组的简单方法


249

连接两个byte数组的简单方法是什么?

说,

byte a[];
byte b[];

如何连接两个byte数组并将其存储在另一个byte数组中?


3
注意:请阿帕奇共享,谷歌的番石榴,System.arrayCopyByteBuffer和-不那么有效,但可读的- ByteArrayOutputStream都被覆盖。在这里给出的答案中,我们有7个以上的重复。请不要再发布任何重复信息。
Maarten Bodewes

Answers:


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

376

最优雅的方法是使用ByteArrayOutputStream

byte a[];
byte b[];

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

byte c[] = outputStream.toByteArray( );

61
@vipw之所以如此优雅,是因为如果/当您希望稍后连接第三个数组时,只需添加行outputStream.write( c );-无需返回并编辑在创建结果字节数组的行。而且,与使用arraycopy方法不同,对数组重新排序很简单。
韦恩·浦田

2
此外,当使用多个2字节数组时,这要容易得多。
gardarh 2013年

3
是否浪费CPU和内存取决于您执行操作的频率。如果每秒十亿次-可以优化它。否则,可读性和可维护性可能是制胜法宝。
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

66

下面是一个使用很好的解决方案番石榴com.google.common.primitives.Bytes

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

此方法的优点在于它具有varargs签名:

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

这意味着您可以在单个方法调用中连接任意数量的数组。


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相对应。看这里。我通过bb.flip(); bb.get(result);代替byte[] result = bb.array();行发行来解决此问题。
DarqueSandu,2015年

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,总是可以byte[]在一行中连接两个(或更多),如下所示:

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

答案相同,但迟到超过一年。使用方法链接,但是最好将其放入现有答案中。
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ía)

也许。我不知道Guava库,所以如果是,最好使用它。您是否检查过非常大的阵列?
Tomasz Przybylski,2016年

1
当我进行测试时,Firts数组的长度是第二个8790688长度的68个元素。
卡洛斯·安德烈斯·加西亚(CarlosAndrésGarcía)

5

对于两个或多个数组,可以使用此简单而干净的实用程序方法:

/**
 * 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
这浪费了内存。对于两个较小的数组,该方法是可以的,但对于更多的数组,肯定会增加垃圾回收器的负担。
Maarten Bodewes

1

合并两个PDF字节数组

如果要合并两个包含PDF的字节数组,则此逻辑将不起作用。我们需要使用第三方工具,例如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);

当然,这也可以使用+加法运算符处理两个以上的字符串串联。


两者"l1"和都ISO_8859_1表示将每个字符编码为单个字节的Western Latin 1字符集。由于不执行多字节转换,因此字符串中的字符将具有与字节相同的值(除了它们始终被解释为正值,例如char是无符号的)。至少对于Oracle提供的运行时,因此将正确地对任何字节进行“解码”,然后再次进行“编码”。

请注意,字符串确实会适当地扩展字节数组,这需要额外的内存。弦线也可能被扣留,因此不容易去除。字符串也是不可变的,因此不能破坏字符串中的值。因此,您不应以这种方式连接敏感数组,也不应将这种方法用于较大的字节数组。由于这种数组级联方法不是常见的解决方案,因此也需要清楚地表明您在做什么。


@MaartenBodewes如果不确定“ l1”(这只是ISO 8859-1的别名),请不要使用“确定”一词。哪个特定的字节值将被清除?至于内存的使用,问题是关于连接两个字节数组的简单方法,而不是最有效的内存数组。
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(...)以任意数量的byte []进行调用。
  • 使用System.arraycopy()了与机器特定的本地代码来实现,以确保高速运转。
  • 用所需的确切大小创建一个新的byte []。
  • int通过重用ilen变量来分配较少的变量。
  • 与常数的比较更快。

注意事项

更好的方法是复制@Jonathan代码。问题出在本机变量数组上,因为当此数据类型传递给另一个函数时,Java会创建新变量。


1
不,那是Wayne的方式,您迟到了5年。
Maarten Bodewes

@MaartenBodewes谢谢您,我今天用您的评论来进行编码,现在变得更加不同,并且性能更好。
Daniel DeLeón19年

1
我不确定这是否有太大关系,因为数组大小在运行时也不会更改,但是现在至少与其他解决方案有所不同。
Maarten Bodewes
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.