Простий спосіб об'єднати два байтові масиви


249

Який простий спосіб об'єднати два byteмасиви?

Сказати,

byte a[];
byte b[];

Як з'єднати два byteмасиви і зберегти його в іншому byteмасиві?


3
Зверніть увагу , будь ласка , що Apache Commons, гуави Google, System.arrayCopy, ByteBufferі тому - не так ефективно , але читаним - 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

Найелегантніший спосіб зробити це за допомогою a ByteArrayOutputStream.

byte a[];
byte b[];

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

byte c[] = outputStream.toByteArray( );

61
@vipw Причина, чому це елегантно, полягає в тому, що якщо / коли ви хочете пізніше об'єднати третій масив, ви просто додаєте рядок outputStream.write( c );- вам не доведеться повертатися назад і редагувати рядок, де ви створюєте масив байтів результатів. Крім того, переупорядкувати масиви просто, на відміну від використання методу масиву копіювання.
— Уейн Урода

2
Крім того, це набагато простіше при роботі з більш ніж двома байтовими масивами.
— gardarh

3
Від того, чи буде витрачатися на процесор і пам'ять, залежить від того, як часто ви робите операцію. Якщо це мільярд разів на секунду - обов'язково, оптимізуйте це. В іншому випадку виграшні міркування можуть бути читабельністю та ремонтопридатністю.
— vikingsteve

5
Якщо споживання пам'яті та / або продуктивність викликає занепокоєння, не забудьте використовувати це a.length + b.lengthяк аргумент для ByteArrayOutputStreamконструктора. Зауважте, що цей метод все одно буде копіювати всі байти в новий масив, який потрібно призначити c[]! Розглянемо ByteBufferметод близького суперника, який не втрачає пам'ять.
— Maarten Bodewes

Я не можу зробити це великими пальцями, тому що це лише фрагмент коду. Тут немає ніяких пояснень основної частини, яка є частиною, яка мені хвилюється (і, думаю, більшість людей хотіла б). Я б із задоволенням дав цьому великий палець, якби було порівняння продуктивності між системою # arrayCopy (Object, int, Object, int, int) та ByteArrayOutputStream # put (байт []), і детально описати, який сценарій найкращий для обох варіантів. Крім того, якщо говорити, відповідь також повинна містити arrayCopy, оскільки це вже інше рішення.
— searchchengine27

66

Ось приємне рішення за допомогою Guava 's 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

@kalefranz Видалено compact()рядок, оскільки вона неправильна.
— Maarten Bodewes

1
Будьте обережні при використанні методу масиву ByteBuffer () - якщо ви абсолютно не знаєте, що ви робите, і ремонтопридатність не є проблемою, немає гарантій, що нульова позиція в байт-буфері завжди відповідає індексу 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 масивів тощо.

Це таким чином дає вам перевагу швидкого коду масиву, який також легко читати та підтримувати.


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

Відповідь така ж, як і ця, але пізніше ніж на 1 рік. Використовує метод ланцюга, але це було б краще вписати в існуючу відповідь.
— Maarten Bodewes

11

Ви можете використовувати сторонні бібліотеки для чистого коду, як Apache Commons Lang, і використовувати його так:

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

1
Я спробував ArrayUtils.addAll(a, b)і byte[] c = Bytes.concat(a, b), але останній швидше.
— Карлос Андрес Гарсія

Може бути. Я не знаю бібліотеки Guava, тому якщо вона є, то краще використовувати її. Ви перевіряли це на дуже великі масиви?
— Томаш Пшибильський

1
Коли я робив тест, масив Фіртса становив 68 елементів довжиною y другий 8790688 довжиною.
— Карлос Андрес Гарсія

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, ця логіка не працюватиме. Нам потрібно використовувати сторонній інструмент, як PDFbox від Apache:

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

дещо поза темою цього питання, але саме це я шукав.
— амос

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вказують набір західних латинських символів 1, який кодує кожен символ як один байт. Оскільки не виконуються багатобайтові переклади, символи в рядку матимуть ті самі значення, що й байти (за винятком того, що вони завжди будуть інтерпретуватися як позитивні значення, як charі без знака). Принаймні, для виконання програми Oracle, будь-який байт буде правильно "декодований" і потім "закодований" знову.

Слідкуйте за тим, щоб рядки ретельно розширювали байтовий масив, вимагаючи додаткової пам'яті. Струни також можуть бути інтернованими, і тому їх легко буде видалити. Рядки також непорушні, тому значення всередині них не можна зруйнувати. Тому ви не повинні поєднувати чутливі масиви таким чином, а також не використовувати цей метод для великих байтових масивів. Надання чіткої вказівки на те, що ви робите, також буде потрібно, оскільки цей метод конкатенації масиву не є звичайним рішенням.


@MaartenBodewes Якщо ви не впевнені в "l1" (що є лише псевдонімом ISO 8859-1), не використовуйте слово "звичайно". Яке конкретно значення байту буде вимкнено? Що стосується використання пам'яті, то питання стосувалося простого способу об'єднання двох байтових масивів, а не про більшості ефективних пам'яті.
— Джон

1
Я відклав кілька попереджень і зробив кілька тестувань. Для латиниці 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()що реалізовано за допомогою машинного коду, для забезпечення високої швидкості роботи.
  • Створіть новий байт [] з точним розміром, який потрібен.
  • Виділіть трохи менше intзмінних шляхом повторного використання змінних iта len.
  • Швидше порівняння з константами.

Майте на увазі :

Кращий спосіб зробити це - скопіювати код @Jonathan . Проблема виникає з власних масивів змінних, оскільки Java створює нові змінні, коли цей тип даних передається іншій функції.


1
Ні, це спосіб Уейна зробити це , ти запізнився на 5 років.
— Maarten Bodewes

@MaartenBodewes Завдяки вам, я використовую ваш коментар для того, щоб здійснювати кодування сьогодні, тепер він відрізняється більш високою продуктивністю.
— Даніель Де Леон

1
Я не впевнений, що це буде мати велике значення, бачачи, що розміри масиву також не змінюються під час виконання, але зараз він відрізняється принаймні від іншого рішення.
— Maarten Bodewes
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.