Modo semplice per concatenare matrici a due byte


249

Qual è il modo semplice per concatenare due bytearray?

Dire,

byte a[];
byte b[];

Come posso concatenare due bytearray e memorizzarlo in un altro bytearray?


3
Si prega di notare che Apache Commons, Guava di Google, System.arrayCopy, ByteBuffere il - non così efficiente ma leggibile - ByteArrayOutputStreamsono stati tutti coperti. Abbiamo più di 7 duplicati delle risposte fornite qui. Per favore, non pubblicare altri duplicati.
Maarten Bodewes,

Risposte:


317

Più semplice:

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

Il modo più elegante per farlo è con a ByteArrayOutputStream.

byte a[];
byte b[];

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

byte c[] = outputStream.toByteArray( );

61
@vipw Il motivo per cui questo è elegante è perché se / quando si desidera concatenare un terzo array in un secondo momento, è sufficiente aggiungere la riga outputStream.write( c );: non è necessario tornare indietro e modificare la riga in cui si crea l'array di byte dei risultati. Inoltre, riordinare le matrici è semplice, diversamente dall'uso del metodo arraycopy.
Wayne Uroda,

2
Inoltre, questo è molto più semplice quando si lavora con più di array a soli 2 byte.
Gardarh,

3
La perdita di CPU e memoria dipende dalla frequenza con cui si esegue l'operazione. Se è un miliardo di volte al secondo, certo, ottimizzalo. Altrimenti, la leggibilità e la manutenibilità potrebbero essere le considerazioni vincenti.
Vienna,

5
Se il consumo di memoria e / o le prestazioni sono un problema, assicurarsi di utilizzare a.length + b.lengthcome argomento per il ByteArrayOutputStreamcostruttore. Si noti che questo metodo copierà ancora tutti i byte in un nuovo array a cui assegnare c[]! Considera il ByteBuffermetodo un contendente stretto, che non spreca memoria.
Maarten Bodewes,

Non posso davvero dare un pollice in su perché questo è solo uno snippet di codice. Non c'è alcuna spiegazione dei pezzi sottostanti qui, che è la parte a cui tengo (e penso che la maggior parte delle persone lo farebbe). Darei volentieri un pollice in su se ci fosse un confronto delle prestazioni tra System # arrayCopy (Object, int, Object, int, int) e ByteArrayOutputStream # put (byte []), e dettagliatamente quale scenario è il migliore per entrambe le opzioni. Inoltre, detto ciò, la risposta dovrebbe includere anche arrayCopy poiché questa è un'altra soluzione.
searchengine27,

66

Ecco una bella soluzione utilizzando Guava 's com.google.common.primitives.Bytes:

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

La cosa grandiosa di questo metodo è che ha una firma varargs:

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

il che significa che è possibile concatenare un numero arbitrario di matrici in una singola chiamata di metodo.


30

Un'altra possibilità sta usando java.nio.ByteBuffer.

Qualcosa di simile a

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

Si noti che l'array deve essere dimensionato in modo appropriato per iniziare, quindi è necessaria la linea di allocazione (poiché array()restituisce semplicemente l'array di supporto, senza tenere conto dell'offset, della posizione o del limite).


3
@click_whir Mi dispiace, ma ReadTheDocs. ByteBuffer.allocate(int)è un metodo statico che restituisce un'istanza java.nio.HeapByteBuffer, una sottoclasse di ByteBuffer. I metodi .put()e .compact()- e qualsiasi altra astronomia - sono curati.
kalefranz,

@kalefranz Riga rimossa compact()in quanto errata.
Maarten Bodewes,

1
Fai attenzione usando il metodo array () di ByteBuffer - a meno che tu non sappia assolutamente cosa stai facendo e la manutenibilità non sia un problema, non ci sono garanzie che la posizione zeroesima nel bytebuffer corrisponda sempre all'indice 0 dell'array byte. Vedi qui . Risolvo questo emettendo bb.flip(); bb.get(result);al posto della byte[] result = bb.array();linea.
DarqueSandu,

1
@DarqueSandu Sebbene sia un buon consiglio in generale , un'attenta lettura del allocatemetodo rivela quanto segue: "La posizione del nuovo buffer sarà zero, il suo limite sarà la sua capacità, il suo segno sarà indefinito e ciascuno dei suoi elementi verrà inizializzato a zero . Avrà un array di supporto e il suo offset di array sarà zero. " Quindi per questo particolare pezzo di codice, dove l' ByteBufferallocazione è allocata internamente, non è un problema.
Maarten Bodewes,

13

Un altro modo è quello di utilizzare una funzione di utilità (è possibile rendere questo un metodo statico di una classe di utilità generica se lo si desidera):

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

Invoca così:

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

Funzionerà anche per concatenare 3, 4, 5 array, ecc.

Farlo in questo modo ti dà il vantaggio di un veloce codice di array che è anche molto facile da leggere e mantenere.


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

Stessa risposta di quella accettata e scusa, con 5 minuti di ritardo.
Maarten Bodewes,

11

Se preferisci ByteBuffercome @kalefranz, c'è sempre la possibilità di concatenare due byte[](o anche più) in una riga, in questo modo:

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

Stessa risposta a questa, ma con più di 1 anno di ritardo. Utilizza il concatenamento del metodo, ma sarebbe meglio inserirlo nella risposta esistente.
Maarten Bodewes,

11

Puoi usare librerie di terze parti per Clean Code come Apache Commons Lang e usarlo come:

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

1
Ho provato ArrayUtils.addAll(a, b)e byte[] c = Bytes.concat(a, b), ma quest'ultimo è più veloce.
Carlos Andrés García,

Può essere. Non conosco la libreria Guava quindi se lo è, è meglio usarla. Hai controllato per array molto grandi?
Tomasz Przybylski,

1
Quando ho fatto il test, l'array Firts aveva 68 elementi di lunghezza y la seconda lunghezza di 8790688.
Carlos Andrés García,

5

Per due o più array, è possibile utilizzare questo metodo di utilità semplice e pulito:

/**
 * 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
Questo spreca memoria. Il metodo sarebbe OK per due array più piccoli, ma sicuramente tassherà il garbage collector per più array.
Maarten Bodewes,

1

Unire due array di byte PDF

Se si stanno unendo array di due byte che contengono PDF, questa logica non funzionerà. Dobbiamo utilizzare uno strumento di terze parti come PDFbox di Apache:

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

un po 'fuori tema per questa domanda, ma è esattamente quello che stavo cercando.
amos,

1

Se non vuoi fare confusione con le dimensioni degli array, usa semplicemente la magia della concatenazione di stringhe:

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

O definisci da qualche parte nel tuo codice

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

e usare

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

Questo, ovviamente, funziona anche con più di due concatenazioni di stringhe usando l' +operatore addizione.


Entrambi "l1"e ISO_8859_1indicano il set di caratteri Western Latin 1 che codifica ogni carattere come un singolo byte. Poiché non vengono eseguite traduzioni multi-byte, i caratteri nella stringa avranno gli stessi valori dei byte (tranne per il fatto che saranno sempre interpretati come valori positivi, poiché charnon è firmato). Almeno per il runtime fornito da Oracle, qualsiasi byte verrà quindi correttamente "decodificato" e quindi "codificato" di nuovo.

Attenzione che le stringhe espandono considerevolmente l'array di byte, richiedendo memoria aggiuntiva. Le stringhe possono anche essere internate e pertanto non saranno facili da rimuovere. Anche le stringhe sono immutabili, quindi i valori al loro interno non possono essere distrutti. Pertanto, non è necessario concatenare gli array sensibili in questo modo né utilizzare questo metodo per array di byte più grandi. Sarebbe anche necessario fornire una chiara indicazione di ciò che si sta facendo, poiché questo metodo di concatenazione di array non è una soluzione comune.


@MaartenBodewes Se non sei sicuro di "l1" (che è solo un alias per ISO 8859-1), non usare la parola "certamente". Quale particolare valore di byte verrà cancellato? Per quanto riguarda l'utilizzo della memoria, la domanda riguardava il modo semplice di concatenare array di due byte, non quello della maggior parte della memoria efficiente.
John McClane,

1
Ho messo giù alcuni avvertimenti e fatto alcuni test. Per Latin 1 e Oracle fornito runtime (11) sembra funzionare. Quindi ho fornito ulteriori informazioni e rimosso il mio commento e il mio voto negativo. Spero che vada bene per te, altrimenti torna indietro.
Maarten Bodewes,

0

Questo è il mio modo di farlo!

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

Caratteristiche :

  • Utilizzare varargs ( ...) per essere chiamato con qualsiasi numero di byte [].
  • Utilizzare System.arraycopy()quello implementato con codice nativo specifico della macchina, per garantire un funzionamento ad alta velocità.
  • Creare un nuovo byte [] con la dimensione esatta che è necessaria.
  • Allocare poco meno intvariabili riutilizzando le variabili ie len.
  • Confronto più rapido con le costanti.

Ricorda :

Il modo migliore per farlo è copiare il codice @Jonathan . Il problema deriva dalle matrici di variabili native, poiché Java crea nuove variabili quando questo tipo di dati viene passato a un'altra funzione.


1
No, questo è il modo di Wayne di farlo , sei in ritardo di 5 anni.
Maarten Bodewes,

@MaartenBodewes Grazie a te, uso il tuo commento per esercitare la codifica oggi, ora è più diverso e con prestazioni migliori.
Daniel De León,

1
Non sono sicuro che avrà troppa importanza, visto che le dimensioni dell'array non cambiano neanche in fase di runtime, ma ora è diverso almeno dall'altra soluzione.
Maarten Bodewes,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.