【问题标题】:Easy way to concatenate two byte arrays连接两个字节数组的简单方法
【发布时间】:2011-07-27 15:44:43
【问题描述】:

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

说,

byte a[];
byte b[];

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

【问题讨论】:

  • 请注意,Apache Commons、Google 的 Guava、System.arrayCopyByteBuffer 和 - 效率不高但可读性强 - ByteArrayOutputStream 都已涵盖。我们在这里给出的答案有超过 7 个重复。请不要再发布任何欺骗。

标签: java arrays concatenation


【解决方案1】:

如果您已经在加载Guava 库,您可以使用来自com.google.common.primitives.Bytes 的静态方法concat(byte[]... arrays)

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

这是“Kevin Bourrillion”的concat(byte[]... arrays) 方法的独立source

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

【讨论】:

    【解决方案2】:

    这是使用Guavacom.google.common.primitives.Bytes 的一个很好的解决方案:

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

    这个方法的好处是它有一个可变参数签名:

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

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

    【讨论】:

    • 这是最简单的方法。顺便说一句,如果您使用的是 Groovy,则没有区别。
    【解决方案3】:

    另一种可能性是使用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() 只返回支持数组,而不考虑偏移量、位置或限制)。

    【讨论】:

    • @click_whir 对不起,阅读文档。 ByteBuffer.allocate(int) 是一个静态方法,它返回实例化的java.nio.HeapByteBufferByteBuffer 的子类。 .put().compact() 方法——以及任何其他抽象性——都得到了处理。
    • @kalefranz 删除了 compact() 行,因为它不正确。
    • 小心使用 ByteBuffer 的 array() 方法 - 除非您绝对知道自己在做什么并且可维护性不是问题,否则无法保证 bytebuffer 中的第 0 个位置始终对应于索引 0的字节数组。见here。我通过发出 bb.flip(); bb.get(result); 而不是 byte[] result = bb.array(); 行来解决这个问题。
    • @DarqueSandu 虽然这是一个很好的建议总的来说,但仔细阅读allocate 方法会发现:“新缓冲区的位置将为零,它的限制将是它的容量,它的标记将是未定义的,它的每个元素都将被初始化为零。它将有一个支持数组,它的数组偏移量将为零。因此,对于这段特定代码,ByteBuffer 是在内部分配的,这不是问题。
    【解决方案4】:

    如果您不想弄乱数组的大小,只需使用字符串连接的魔力:

    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 都表示将每个字符编码为单个字节的西拉丁语 1 字符集。由于不执行多字节转换,字符串中的字符将具有与字节相同的值(除了它们将始终被解释为正值,因为char 是无符号的)。至少对于 Oracle 提供的运行时,任何字节都将因此被正确“解码”,然后再次“编码”。

    请注意,字符串确实会周到地扩展字节数组,需要额外的内存。字符串也可能被扣留,因此不容易被移除。字符串也是不可变的,因此其中的值不能被破坏。因此,您不应以这种方式连接敏感数组,也不应将此方法用于较大的字节数组。还需要明确说明您正在做什么,因为这种数组连接方法不是常见的解决方案。

    【讨论】:

    • @MaartenBodewes 如果您不确定“l1”(它只是 ISO 8859-1 的别名),请不要使用“肯定”这个词。哪个特定的字节值将被清除?至于内存使用,问题是关于连接两个字节数组的简单方法,而不是关于最节省内存的方法。
    • 我已经提出了一些警告并进行了一些测试。对于 Latin 1 和 Oracle 提供的运行时 (11),这似乎确实有效。所以我提供了额外的信息并删除了我的评论和反对票。我希望你没问题,否则请回滚。
    【解决方案5】:

    这是我的方法!

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

    特点

    • 使用可变参数 (...) 以任意数量的 byte[] 调用。
    • 使用用机器特定的本机代码实现的System.arraycopy(),以确保高速运行。
    • 创建一个具有所需确切大小的新字节[]。
    • 通过重用ilen 变量,分配更少的int 变量。
    • 与常数比较更快。

    记住

    更好的方法是复制@Jonathan code。问题来自原生变量数组,因为当这种数据类型传递给另一个函数时,Java 会创建新变量。

    【讨论】:

    • 不,那是Wayne's way to do it,你迟到了 5 年。
    • @MaartenBodewes 谢谢你,我今天用你的评论来练习编码,现在更加不同并且性能更好。
    • 我不确定它会不会太重要,因为数组大小在运行时也不会改变,但它现在至少与其他解决方案不同。
    【解决方案6】:

    最优雅的方法是使用ByteArrayOutputStream

    byte a[];
    byte b[];
    
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
    outputStream.write( a );
    outputStream.write( b );
    
    byte c[] = outputStream.toByteArray( );
    

    【讨论】:

    • @vipw 这很优雅的原因是,如果/当您希望稍后连接第三个数组时,您只需添加行 outputStream.write( c ); - 您不必返回并编辑创建结果字节数组的行。此外,与使用 arraycopy 方法不同,对数组重新排序很简单。
    • 此外,当使用超过 2 字节的数组时,这要容易得多。
    • 是否浪费cpu和内存取决于你执行操作的频率。如果它是每秒十亿次 - 当然,优化它。否则,可读性和可维护性可能是获胜的考虑因素。
    • 如果内存消耗和/或性能是一个问题,请务必使用a.length + b.length 作为ByteArrayOutputStream 构造函数的参数。请注意,此方法仍会将所有字节复制到一个新数组以分配给c[]!考虑ByteBuffer 方法是一个有力的竞争者,它不会浪费内存。
    • 我真的不能竖起大拇指,因为这只是一个代码 sn-p。这里没有对底层部分的解释,这是我关心的部分(我想大多数人都会关心)。如果在 System#arrayCopy(Object, int, Object, int, int) 和 ByteArrayOutputStream#put(byte[]) 之间进行了性能比较,我很乐意对此表示赞赏,并详细说明哪种方案最适合这两个选项。另外,话虽如此,答案还应该包括 arrayCopy,因为这是另一种解决方案。
    【解决方案7】:

    合并两个 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();
    

    【讨论】:

    • 这个问题有点离题,但正是我想要的。
    【解决方案8】:

    您可以将第三方库用于清洁代码,例如 Apache Commons Lang,并像这样使用它:

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

    【讨论】:

    • 我试过ArrayUtils.addAll(a, b)byte[] c = Bytes.concat(a, b),但后者更快。
    • 也许吧。我不知道 Guava 库,所以如果是的话,最好使用它。你检查过非常大的数组吗?
    • 当我进行测试时,Firts 数组是 68 个元素长度 y 第二个 8790688 长度。
    【解决方案9】:

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

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

    【讨论】:

    • 这会浪费内存。该方法适用于两个较小的数组,但肯定会对更多数组的垃圾收集器征税。
    【解决方案10】:

    如果您喜欢ByteBuffer 和@kalefranz,则始终可以在一行中连接两个byte[](甚至更多),如下所示:

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

    【讨论】:

    • 答案与this one 相同,但晚了一年多。使用方法链接,但最好将其放入现有答案中。
    【解决方案11】:

    另一种方法是使用实​​用程序函数(如果您愿意,可以将其设为通用实用程序类的静态方法):

    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 个数组等。

    以这种方式为您提供快速数组复制代码的优势,该代码也非常易于阅读和维护。

    【讨论】:

      【解决方案12】:
      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);
      

      【讨论】:

      • accepted one 的答案相同,抱歉,迟到了 5 分钟。
      【解决方案13】:

      最直接的:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-10
        • 2014-09-05
        • 1970-01-01
        • 2014-11-23
        相关资源
        最近更新 更多