Cách dễ dàng để nối hai mảng byte


249

Cách dễ dàng để nối hai byte mảng là gì?

Nói,

byte a[];
byte b[];

Làm thế nào để tôi ghép hai bytemảng và lưu trữ nó trong một bytemảng khác ?


3
Lưu ý xin vui lòng mà Apache Commons, ổi của Google, System.arrayCopy, ByteBuffervà - không quá hiệu quả nhưng có thể đọc được - ByteArrayOutputStreamtất cả đều được bao phủ. Chúng tôi đã nhận được hơn 7 bản sao của các câu trả lời được đưa ra ở đây. Xin vui lòng không đăng bất kỳ bản sao nào nữa.
Maarten Bodewes

Câu trả lời:


317

Đơn giản nhất:

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

Cách thanh lịch nhất để làm điều này là với a ByteArrayOutputStream.

byte a[];
byte b[];

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

byte c[] = outputStream.toByteArray( );

61
@vipw Lý do tại sao điều này thanh lịch là bởi vì nếu / khi bạn muốn nối một mảng thứ ba sau đó, bạn chỉ cần thêm dòng outputStream.write( c );- bạn không phải quay lại và chỉnh sửa dòng nơi bạn tạo mảng byte kết quả. Ngoài ra, việc sắp xếp lại các mảng rất đơn giản, không giống như sử dụng phương pháp mảng.
Wayne Uroda

2
Ngoài ra, điều này dễ dàng hơn nhiều khi làm việc với nhiều hơn chỉ 2 mảng.
vườn

3
Việc nó có làm lãng phí cpu và bộ nhớ hay không phụ thuộc vào tần suất bạn thực hiện thao tác. Nếu đó là một tỷ lần một giây - chắc chắn, hãy tối ưu hóa nó. Mặt khác, khả năng đọc và bảo trì có thể là những cân nhắc chiến thắng.
vikingsteve 6/12/13

5
Nếu mức tiêu thụ bộ nhớ và / hoặc hiệu suất là một mối quan tâm, hãy chắc chắn sử dụng a.length + b.lengthlàm đối số cho hàm ByteArrayOutputStreamtạo. Lưu ý rằng phương thức này vẫn sẽ sao chép tất cả các byte sang một mảng mới để gán cho c[]! Xem xét ByteBufferphương pháp một ứng cử viên gần gũi, điều đó không lãng phí bộ nhớ.
Maarten Bodewes

Tôi thực sự không thể đưa ra ý kiến ​​này vì đây chỉ là một đoạn mã. Không có lời giải thích về các phần cơ bản ở đây, đó là phần tôi quan tâm (và tôi nghĩ rằng hầu hết mọi người sẽ). Tôi sẵn sàng đưa ra ý kiến ​​này nếu có sự so sánh hiệu năng giữa System # ArrayCopy (Object, int, Object, int, int) và ByteArrayOutputStream # put (byte []) và chi tiết kịch bản nào là tốt nhất cho cả hai tùy chọn. Ngoài ra, điều đó đang được nói, câu trả lời cũng nên bao gồm ArrayCopy vì đó là một giải pháp khác.
searchengine27

66

Dưới đây là một giải pháp tốt đẹp sử dụng ổi 's com.google.common.primitives.Bytes:

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

Điều tuyệt vời của phương pháp này là nó có chữ ký varargs:

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

điều đó có nghĩa là bạn có thể nối một số mảng tùy ý trong một lệnh gọi phương thức.


30

Một khả năng khác là sử dụng java.nio.ByteBuffer .

Cái gì đó như

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

Lưu ý rằng mảng phải có kích thước phù hợp để bắt đầu, do đó, dòng phân bổ là bắt buộc (vì array()chỉ cần trả về mảng sao lưu, mà không lấy phần bù, vị trí hoặc giới hạn vào tài khoản).


3
@click_whir Xin lỗi người đàn ông, nhưng ReadTheDocs. ByteBuffer.allocate(int)là một phương thức tĩnh trả về một khởi tạo java.nio.HeapByteBuffer, một lớp con của ByteBuffer. Các phương thức .put().compact()- và bất kỳ tính trừu tượng nào khác - được quan tâm.
kalefranz

@kalefranz Đã xóa compact()dòng vì nó không chính xác.
Maarten Bodewes

1
Hãy cẩn thận khi sử dụng phương thức mảng () của ByteBuffer - trừ khi bạn hoàn toàn biết bạn đang làm gì và khả năng bảo trì không phải là vấn đề, không có gì đảm bảo rằng vị trí zeroeth trong bytebuffer luôn tương ứng với chỉ số 0 của mảng byte. Xem tại đây . Tôi giải quyết điều này bằng cách phát hành bb.flip(); bb.get(result);thay cho byte[] result = bb.array();dòng.
DarqueSandu

1
@DarqueSandu Mặc dù đó là lời khuyên tốt nói chung , việc đọc kỹ allocatephương pháp cho thấy như sau: "Vị trí của bộ đệm mới sẽ bằng 0, giới hạn của nó sẽ là khả năng của nó, dấu của nó sẽ không được xác định và mỗi phần tử của nó sẽ được khởi tạo thành 0 Nó sẽ có một mảng sao lưu và bù mảng của nó sẽ bằng không. " Vì vậy, đối với đoạn mã đặc biệt này , nơi ByteBufferphân bổ nội bộ, đó không phải là vấn đề.
Maarten Bodewes

13

Một cách khác là sử dụng một hàm tiện ích (bạn có thể biến nó thành một phương thức tĩnh của một lớp tiện ích chung nếu bạn muốn):

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

Gọi như vậy:

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

Nó cũng sẽ làm việc để ghép 3, 4, 5 mảng, v.v.

Làm theo cách này cung cấp cho bạn lợi thế của mã mảng nhanh, cũng rất dễ đọc và duy trì.


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

Câu trả lời tương tự như được chấp nhận và xin lỗi, trễ 5 phút.
Maarten Bodewes

11

Nếu bạn thích ByteBuffernhư @kalefranz, luôn có khả năng ghép hai byte[](hoặc thậm chí nhiều hơn) trong một dòng, như sau:

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

Câu trả lời tương tự như thế này nhưng muộn hơn 1 năm. Sử dụng phương pháp xâu chuỗi, nhưng điều đó sẽ tốt hơn để đưa vào câu trả lời hiện có.
Maarten Bodewes

11

Bạn có thể sử dụng các thư viện của bên thứ ba cho Clean Code như Apache Commons Lang và sử dụng nó như sau:

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

1
Tôi đã thử ArrayUtils.addAll(a, b)byte[] c = Bytes.concat(a, b), nhưng cái sau nhanh hơn.
Carlos Andrés García

Có lẽ. Tôi không biết thư viện Guava vì vậy nếu có, tốt hơn là sử dụng nó. Bạn đã kiểm tra nó cho các mảng rất lớn?
Tomasz Przybylski

1
Khi tôi thực hiện kiểm tra, mảng Fumps có chiều dài 68 phần tử y với chiều dài 8790688 thứ hai.
Carlos Andrés García

5

Đối với hai hoặc nhiều mảng, phương pháp tiện ích đơn giản và sạch sẽ này có thể được sử dụng:

/**
 * 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
Điều này làm lãng phí bộ nhớ. Phương pháp này sẽ ổn đối với hai mảng nhỏ hơn, nhưng chắc chắn nó sẽ đánh thuế người thu gom rác cho nhiều mảng hơn.
Maarten Bodewes

1

Hợp nhất hai mảng byte PDF

Nếu bạn hợp nhất hai mảng byte chứa PDF, logic này sẽ không hoạt động. Chúng tôi cần sử dụng một công cụ của bên thứ ba như PDFbox từ Apache:

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

một chút lạc đề cho câu hỏi này, nhưng đó chính xác là những gì tôi đang tìm kiếm.
amos

1

Nếu bạn không muốn gây rối với kích thước của mảng, chỉ cần sử dụng phép thuật nối chuỗi:

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

Hoặc xác định một nơi nào đó trong mã của bạn

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

Và sử dụng

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

Tất nhiên, điều này cũng hoạt động với hơn hai chuỗi nối bằng cách sử dụng +toán tử cộng.


Cả hai "l1"ISO_8859_1chỉ ra bộ ký tự Western Latin 1 mã hóa mỗi ký tự dưới dạng một byte. Vì không có bản dịch nhiều byte được thực hiện, các ký tự trong chuỗi sẽ có cùng giá trị với byte (ngoại trừ việc chúng sẽ luôn được hiểu là giá trị dương, nhưchar không dấu). Ít nhất là đối với thời gian chạy do Oracle cung cấp, do đó, bất kỳ byte nào cũng sẽ được "giải mã" chính xác và sau đó "được mã hóa" lại.

Coi chừng các chuỗi mở rộng mảng byte một cách thận trọng, đòi hỏi bộ nhớ bổ sung. Chuỗi cũng có thể được thực tập và do đó sẽ không dễ dàng bị loại bỏ. Chuỗi cũng bất biến, vì vậy các giá trị bên trong chúng không thể bị phá hủy. Do đó, bạn không nên ghép các mảng nhạy cảm theo cách này cũng như không nên sử dụng phương thức này cho các mảng byte lớn hơn. Đưa ra một dấu hiệu rõ ràng về những gì bạn đang làm cũng sẽ được yêu cầu, vì phương pháp nối mảng này không phải là một giải pháp phổ biến.


@MaartenBodewes Nếu bạn không chắc chắn về "l1" (vốn chỉ là bí danh cho ISO 8859-1), đừng sử dụng từ "chắc chắn". Giá trị byte cụ thể nào sẽ bị xóa sổ? Đối với việc sử dụng bộ nhớ, câu hỏi là về cách dễ dàng để ghép hai mảng byte, chứ không phải về hầu hết hiệu quả của bộ nhớ.
John McClane

1
Tôi đã đưa ra một số cảnh báo và đã làm một số thử nghiệm. Đối với Latin 1 và Oracle cung cấp thời gian chạy (11), điều này dường như hoạt động. Vì vậy, tôi đã cung cấp thêm thông tin và xóa bình luận và downvote của tôi. Tôi hy vọng điều đó tốt cho bạn, nếu không xin vui lòng quay lại.
Maarten Bodewes

0

Đây là cách của tôi để làm điều đó!

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

Các tính năng :

  • Sử dụng varargs (... ) để được gọi với bất kỳ số byte [] nào.
  • Sử dụng System.arraycopy()được thực hiện với mã gốc cụ thể của máy, để đảm bảo hoạt động tốc độ cao.
  • Tạo một byte mới [] với kích thước chính xác cần thiết.
  • Phân bổ intcác biến ít hơn bằng cách sử dụng lại ilencác biến.
  • So sánh nhanh hơn với hằng số.

Hãy ghi nhớ :

Cách tốt hơn để làm điều này là bằng cách sao chép mã @Jonathan . Vấn đề xuất phát từ các mảng biến tự nhiên, bởi vì Java tạo các biến mới khi kiểu dữ liệu này được truyền cho hàm khác.


1
Không, đó là cách của Wayne để làm điều đó , bạn trễ 5 năm.
Maarten Bodewes

@MaartenBodewes Nhờ có bạn, tôi sử dụng nhận xét của bạn để thực hiện mã hóa ngày hôm nay, giờ đã khác hơn và có hiệu suất tốt hơn.
Daniel De León

1
Tôi không chắc nó sẽ quan trọng quá nhiều, khi thấy rằng kích thước mảng cũng không thay đổi trong thời gian chạy, nhưng bây giờ nó khác ít nhất so với giải pháp khác.
Maarten Bodewes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.