Einfache Möglichkeit, zwei Byte-Arrays zu verketten


249

Was ist der einfache Weg, um zwei byteArrays zu verketten ?

Sagen,

byte a[];
byte b[];

Wie verkette ich zwei byteArrays und speichere sie in einem anderen byteArray?


3
Beachten Sie bitte , dass Apache Commons, Googles Guava, System.arrayCopy, ByteBufferund die - nicht so effizient , aber lesbar - ByteArrayOutputStreamhaben alle abgedeckt. Wir haben mehr als 7 Dupes der hier gegebenen Antworten. Bitte poste keine Dupes mehr.
Maarten Bodewes

Antworten:


317

Am einfachsten:

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

Der eleganteste Weg, dies zu tun, ist mit einem ByteArrayOutputStream.

byte a[];
byte b[];

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

byte c[] = outputStream.toByteArray( );

61
@vipw Der Grund, warum dies elegant ist, liegt darin, dass Sie, wenn Sie ein drittes Array später verketten möchten, einfach die Zeile hinzufügen outputStream.write( c );- Sie müssen nicht zurückgehen und die Zeile bearbeiten, in der Sie das Ergebnisbyte-Array erstellen. Außerdem ist die Neuanordnung der Arrays im Gegensatz zur Arraycopy-Methode einfach.
Wayne Uroda

2
Darüber hinaus ist dies viel einfacher, wenn Sie mit mehr als nur 2-Byte-Arrays arbeiten.
Gardarh

3
Ob es CPU und Speicher verschwendet, hängt davon ab, wie oft Sie den Vorgang ausführen. Wenn es eine Milliarde Mal pro Sekunde ist - optimieren Sie es. Andernfalls könnten Lesbarkeit und Wartbarkeit die entscheidenden Überlegungen sein.
Wikingersteve

5
Wenn der Speicherverbrauch und / oder die Leistung ein Problem darstellen, müssen Sie dies a.length + b.lengthals Argument für den ByteArrayOutputStreamKonstruktor verwenden. Beachten Sie, dass diese Methode weiterhin alle Bytes in ein neues Array kopiert, dem sie zugewiesen werden soll c[]! Betrachten Sie die ByteBufferMethode als einen engen Konkurrenten, der keinen Speicher verschwendet.
Maarten Bodewes

Ich kann das nicht wirklich aufgeben, weil dies nur ein Code-Snippet ist. Es gibt hier keine Erklärung für die zugrunde liegenden Teile, was mir wichtig ist (und ich denke, die meisten Leute würden das tun). Ich würde gerne einen Daumen hoch geben, wenn es einen Leistungsvergleich zwischen System # arrayCopy (Object, int, Object, int, int) und ByteArrayOutputStream # put (byte []) gäbe, und detailliert beschreiben, welches Szenario für beide Optionen am besten geeignet ist. Abgesehen davon sollte die Antwort auch arrayCopy enthalten, da dies eine andere Lösung ist.
Searchengine27

66

Hier ist eine schöne Lösung mit Guava ‚s com.google.common.primitives.Bytes:

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

Das Tolle an dieser Methode ist, dass sie eine varargs-Signatur hat:

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

Dies bedeutet, dass Sie eine beliebige Anzahl von Arrays in einem einzigen Methodenaufruf verketten können.


30

Eine andere Möglichkeit ist die Verwendung java.nio.ByteBuffer.

Etwas wie

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

Beachten Sie, dass das Array zunächst eine angemessene Größe haben muss, sodass die Zuordnungszeile erforderlich ist (da array()das Backing-Array einfach zurückgegeben wird, ohne den Versatz, die Position oder das Limit zu berücksichtigen).


3
@click_whir Sorry Mann, aber ReadTheDocs. ByteBuffer.allocate(int)ist eine statische Methode, die eine instanziierte java.nio.HeapByteBufferUnterklasse von zurückgibt ByteBuffer. Für die .put()und .compact()Methoden - und jede andere Abstraktheit - wird gesorgt.
Kalefranz

@kalefranz compact()Zeile entfernt , da sie falsch ist.
Maarten Bodewes

1
Seien Sie vorsichtig bei der Verwendung der Array () -Methode von ByteBuffer. Wenn Sie nicht genau wissen, was Sie tun, und die Wartbarkeit kein Problem darstellt, gibt es keine Garantie dafür, dass die nullte Position im Bytebuffer immer dem Index 0 des Byte-Arrays entspricht. Siehe hier . Ich löse dieses Problem, indem ich bb.flip(); bb.get(result);anstelle der byte[] result = bb.array();Zeile ausstelle .
DarqueSandu

1
@DarqueSandu Obwohl dies im Allgemeinen ein guter Rat ist , allocatezeigt das sorgfältige Lesen der Methode Folgendes: "Die Position des neuen Puffers ist Null, seine Grenze ist seine Kapazität, seine Markierung ist undefiniert und jedes seiner Elemente wird auf Null initialisiert Es wird ein Hintergrundarray haben und sein Array-Offset wird Null sein. " Für diesen speziellen Code, bei dem der Code ByteBufferintern zugewiesen wird, ist dies kein Problem.
Maarten Bodewes

13

Eine andere Möglichkeit ist die Verwendung einer Dienstprogrammfunktion (Sie können dies zu einer statischen Methode einer generischen Dienstprogrammklasse machen, wenn Sie möchten):

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

Rufen Sie so auf:

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

Es funktioniert auch zum Verketten von 3, 4, 5 Arrays usw.

Auf diese Weise erhalten Sie den Vorteil eines schnellen Arraycopy-Codes, der auch sehr einfach zu lesen und zu warten ist.


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

Gleiche Antwort wie die akzeptierte und sorry, 5 Minuten zu spät.
Maarten Bodewes

11

Wenn Sie ByteBuffer@kalefranz bevorzugen , besteht immer die Möglichkeit, zwei byte[](oder sogar mehr) in einer Zeile wie folgt zu verketten :

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

Gleiche Antwort wie diese, aber mehr als 1 Jahr zu spät. Verwendet Methodenverkettung, aber das wäre besser in die vorhandene Antwort zu integrieren.
Maarten Bodewes

11

Sie können Bibliotheken von Drittanbietern für Clean Code wie Apache Commons Lang verwenden und wie folgt verwenden:

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

1
Ich habe es versucht ArrayUtils.addAll(a, b)und byte[] c = Bytes.concat(a, b), aber letzteres ist schneller.
Carlos Andrés García

Vielleicht. Ich kenne die Guava-Bibliothek nicht. Wenn ja, ist es besser, sie zu verwenden. Haben Sie es auf sehr große Arrays überprüft?
Tomasz Przybylski

1
Als ich den Test durchführte, hatte das Firts-Array eine Länge von 68 Elementen und die zweite Länge von 8790688.
Carlos Andrés García

5

Für zwei oder mehrere Arrays kann diese einfache und saubere Dienstprogrammmethode verwendet werden:

/**
 * 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
Dies verschwendet Speicher. Die Methode wäre für zwei kleinere Arrays in Ordnung, aber sie belastet den Garbage Collector mit Sicherheit für mehr Arrays.
Maarten Bodewes

1

Führen Sie zwei PDF-Byte-Arrays zusammen

Wenn Sie zwei Byte-Arrays zusammenführen, die PDF enthalten, funktioniert diese Logik nicht. Wir müssen ein Drittanbieter-Tool wie PDFbox von Apache verwenden:

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

Ein bisschen abseits des Themas zu dieser Frage, aber genau das, wonach ich gesucht habe.
Amos

1

Wenn Sie sich nicht mit den Größen von Arrays herumschlagen möchten, verwenden Sie einfach die Magie der Zeichenfolgenverkettung:

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

Oder definieren Sie irgendwo in Ihrem Code

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

und verwenden

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

Dies funktioniert natürlich auch mit mehr als zwei Zeichenfolgenverkettungen unter Verwendung des +Additionsoperators.


Beide "l1"und ISO_8859_1geben den Western Latin 1-Zeichensatz an, der jedes Zeichen als einzelnes Byte codiert. Da keine Mehrbyte-Übersetzungen ausgeführt werden, haben die Zeichen in der Zeichenfolge dieselben Werte wie die Bytes (außer dass sie immer als positive Werte interpretiert werden, alschar nicht signiert sind). Zumindest für die von Oracle bereitgestellte Laufzeit wird daher jedes Byte korrekt "decodiert" und dann erneut "codiert".

Beachten Sie, dass Zeichenfolgen das Byte-Array erheblich erweitern und zusätzlichen Speicher benötigen. Saiten können auch interniert werden und lassen sich daher nicht leicht entfernen. Zeichenfolgen sind ebenfalls unveränderlich, sodass die darin enthaltenen Werte nicht zerstört werden können. Sie sollten daher vertrauliche Arrays nicht auf diese Weise verketten oder diese Methode für Arrays mit größeren Bytes verwenden. Es wäre auch erforderlich, einen klaren Hinweis darauf zu geben, was Sie tun, da diese Methode der Array-Verkettung keine übliche Lösung ist.


@ MaartenBodewes Wenn Sie sich bei "l1" (was nur ein Alias ​​für ISO 8859-1 ist) nicht sicher sind, verwenden Sie nicht das Wort "sicher". Welcher bestimmte Bytewert wird gelöscht? Bei der Speichernutzung ging es um die einfache Möglichkeit, zwei Byte-Arrays zu verketten, nicht um die speichereffizienteste.
John McClane

1
Ich habe einige Warnungen niedergelegt und einige Tests durchgeführt. Für Latin 1 und die von Oracle bereitgestellte Laufzeit (11) scheint dies zu funktionieren. Also habe ich zusätzliche Informationen bereitgestellt und meinen Kommentar und meine Ablehnung entfernt. Ich hoffe, das ist in Ordnung für Sie, sonst rollen Sie bitte zurück.
Maarten Bodewes

0

Das ist mein Weg, es zu tun!

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

Eigenschaften :

  • Verwenden Sie varargs ( ...), um mit einer beliebigen Anzahl von Bytes [] aufgerufen zu werden.
  • Verwenden Sie System.arraycopy()diese Option mit maschinenspezifischem nativem Code, um einen Hochgeschwindigkeitsbetrieb sicherzustellen.
  • Erstellen Sie ein neues Byte [] mit der genauen Größe, die es benötigt.
  • Weisen Sie etwas weniger intVariablen zu, indem Sie die Variablen iund wiederverwenden len.
  • Schnellerer Vergleich mit Konstanten.

Denken Sie daran :

Der bessere Weg, dies zu tun, ist das Kopieren des @ Jonathan-Codes . Das Problem tritt bei den nativen Variablenarrays auf, da Java neue Variablen erstellt, wenn dieser Datentyp an eine andere Funktion übergeben wird.


1
Nein, so macht es Wayne , du bist 5 Jahre zu spät.
Maarten Bodewes

@ MaartenBodewes Dank dir benutze ich deinen Kommentar, um das Codieren heute zu üben, jetzt ist es anders und mit besserer Leistung.
Daniel De León

1
Ich bin mir nicht sicher, ob es zu wichtig sein wird, da sich die Arraygrößen auch zur Laufzeit nicht ändern, aber es unterscheidet sich jetzt zumindest von der anderen Lösung.
Maarten Bodewes
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.