Manera fácil de concatenar dos conjuntos de bytes

249

¿Cuál es la forma fácil de concatenar dos bytematrices?

Decir,

byte a[];
byte b[];

¿Cómo concateno dos bytematrices y las almaceno en otra bytematriz?

androidGuy
fuente
3
Nota por favor que Apache Commons, guayaba, de Google System.arrayCopy, ByteBuffery el - no es tan eficiente, pero legible - ByteArrayOutputStreamtodos se han cubierto. Tenemos más de 7 engaños de las respuestas dadas aquí. Por favor, no publique más engaños.
Maarten Bodewes

Respuestas:

317

Más directo:

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);
Jonathan
fuente
377

La forma más elegante de hacer esto es con un ByteArrayOutputStream.

byte a[];
byte b[];

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

byte c[] = outputStream.toByteArray( );
Kevin
fuente
61
@vipw La razón por la que esto es elegante es porque si / cuando desea concatenar una tercera matriz más tarde, simplemente agrega la línea outputStream.write( c );; no tiene que volver atrás y editar la línea donde crea la matriz de bytes de resultado. Además, reordenar las matrices es simple, a diferencia del uso del método arraycopy.
Wayne Uroda
2
Además, esto es mucho más fácil cuando se trabaja con más de solo 2 bytes.
gardarh
3
Si está desperdiciando CPU y memoria depende de la frecuencia con la que realice la operación. Si es mil millones de veces por segundo, seguro, optimícelo. De lo contrario, la legibilidad y la facilidad de mantenimiento podrían ser las consideraciones ganadoras.
vikingsteve
55
Si el consumo de memoria y / o el rendimiento es un problema, asegúrese de usarlo a.length + b.lengthcomo argumento para el ByteArrayOutputStreamconstructor. Tenga en cuenta que este método todavía copiará todos los bytes a una nueva matriz para asignarlos c[]. Considere el ByteBuffermétodo como un competidor cercano, que no desperdicie memoria.
Maarten Bodewes
Realmente no puedo aprobar esto porque esto es solo un fragmento de código. No hay una explicación de las piezas subyacentes aquí, que es la parte que me interesa (y creo que la mayoría de la gente lo haría). Con mucho gusto le daría un pulgar hacia arriba si hubiera una comparación de rendimiento entre System # arrayCopy (Object, int, Object, int, int) y ByteArrayOutputStream # put (byte []), y detallara qué escenario es el mejor para ambas opciones. Además, dicho esto, la respuesta también debe incluir arrayCopy ya que esa es otra solución.
searchengine27
66

He aquí una buena solución usando la guayaba 's com.google.common.primitives.Bytes:

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

Lo mejor de este método es que tiene una firma varargs:

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

lo que significa que puede concatenar un número arbitrario de matrices en una sola llamada de método.

Zoltán
fuente
30

Otra posibilidad es usar java.nio.ByteBuffer.

Algo como

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

Tenga en cuenta que la matriz debe tener el tamaño adecuado para comenzar, por lo que se requiere la línea de asignación (ya que array()simplemente devuelve la matriz de respaldo, sin tener en cuenta el desplazamiento, la posición o el límite).

kalefranz
fuente
3
@click_whir Lo siento hombre, pero ReadTheDocs. ByteBuffer.allocate(int)es un método estático que devuelve una instancia java.nio.HeapByteBuffer, una subclase de ByteBuffer. Se atiende a los métodos .put()y .compact(), y a cualquier otra abstracción.
kalefranz
@kalefranz Se eliminó la compact()línea porque es incorrecta.
Maarten Bodewes
1
Tenga cuidado al usar el método de matriz () de ByteBuffer: a menos que sepa absolutamente lo que está haciendo y la mantenibilidad no es un problema, no hay garantías de que la posición cero en el búfer de bytes siempre corresponda al índice 0 de la matriz de bytes. Ver aquí . Resuelvo esto emitiendo bb.flip(); bb.get(result);en lugar de la byte[] result = bb.array();línea.
DarqueSandu
1
@DarqueSandu Aunque eso es un buen consejo en general , la lectura cuidadosa del allocatemétodo revela lo siguiente: "La posición del nuevo buffer será cero, su límite será su capacidad, su marca será indefinida y cada uno de sus elementos se inicializará a cero . Tendrá una matriz de respaldo y su compensación de matriz será cero ". Entonces, para este fragmento de código en particular , donde ByteBufferse asigna internamente, no es un problema.
Maarten Bodewes
13

Otra forma es usar una función de utilidad (si lo desea, puede hacer que sea un método estático de una clase de utilidad genérica):

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

Invocar así:

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

También funcionará para concatenar 3, 4, 5 matrices, etc.

Hacerlo de esta manera le brinda la ventaja de un código de matriz rápida que también es muy fácil de leer y mantener.

Wayne Uroda
fuente
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);
James.Xu
fuente
Misma respuesta que la aceptada y lo siento, 5 minutos tarde.
Maarten Bodewes
11

Si prefiere ByteBuffercomo @kalefranz, siempre existe la posibilidad de concatenar dos byte[](o incluso más) en una línea, como esta:

byte[] c = ByteBuffer.allocate(a.length+b.length).put(a).put(b).array();
Zeus
fuente
La misma respuesta que esta pero con más de 1 año de retraso. Utiliza el método de encadenamiento, pero sería mejor ponerlo en la respuesta existente.
Maarten Bodewes
11

Puede usar bibliotecas de terceros para Clean Code como Apache Commons Lang y usarlo como:

byte[] bytes = ArrayUtils.addAll(a, b);
Tomasz Przybylski
fuente
1
Lo intenté ArrayUtils.addAll(a, b)y byte[] c = Bytes.concat(a, b), pero este último es más rápido.
Carlos Andrés García
Tal vez. No conozco la biblioteca Guava, así que si es así, es mejor usarla. ¿Lo comprobó para matrices muy grandes?
Tomasz Przybylski
1
Cuando hice la prueba, la matriz de Firts tenía 68 elementos de longitud y la segunda longitud 8790688.
Carlos Andrés García
5

Para dos o múltiples matrices, se puede utilizar este método de utilidad simple y limpio:

/**
 * 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();
}
Jeroen Meulemeester
fuente
1
Esto desperdicia memoria. El método estaría bien para dos matrices más pequeñas, pero ciertamente gravará al recolector de basura para obtener más matrices.
Maarten Bodewes
1

Combinar dos conjuntos de bytes PDF

Si está fusionando dos conjuntos de bytes que contienen PDF, esta lógica no funcionará. Necesitamos usar una herramienta de terceros como PDFbox de Apache:

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
mergePdf.addSource(new ByteArrayInputStream(a));
mergePdf.addSource(new ByteArrayInputStream(b));
mergePdf.setDestinationStream(byteArrayOutputStream);
mergePdf.mergeDocuments();
c = byteArrayOutputStream.toByteArray();
Bala Vignesh B
fuente
un poco fuera de tema a esta pregunta, pero es exactamente lo que estaba buscando.
amos
1

Si no desea meterse con los tamaños de las matrices, simplemente use la magia de la concatenación de cadenas:

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

O defina en algún lugar de su código

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

y use

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

Esto, por supuesto, también funciona con más de dos concatenaciones de cadenas usando el +operador de suma.


Ambos "l1"e ISO_8859_1indican el conjunto de caracteres del latín occidental 1 que codifica cada carácter como un solo byte. Como no se realizan traducciones de varios bytes, los caracteres de la cadena tendrán los mismos valores que los bytes (excepto que siempre se interpretarán como valores positivos, ya charque no están firmados). Al menos para el tiempo de ejecución proporcionado por Oracle, cualquier byte se "decodificará" correctamente y luego "codificará" nuevamente.

Tenga en cuenta que las cadenas expanden la matriz de bytes con consideración, lo que requiere memoria adicional. Las cadenas también pueden ser internadas y, por lo tanto, no serán fáciles de eliminar. Las cadenas también son inmutables, por lo que los valores dentro de ellas no se pueden destruir. Por lo tanto, no debe concatenar matrices sensibles de esta manera ni utilizar este método para matrices de bytes más grandes. También sería necesario dar una indicación clara de lo que está haciendo, ya que este método de concatenación de matrices no es una solución común.

John McClane
fuente
@MaartenBodewes Si no está seguro acerca de "l1" (que es solo un alias para ISO 8859-1), no use la palabra "ciertamente". ¿Qué valor de byte particular será eliminado? En cuanto al uso de la memoria, la pregunta era sobre la forma fácil de concatenar dos conjuntos de bytes, no sobre la mayoría de la memoria eficiente.
John McClane
1
He puesto algunas advertencias e hice algunas pruebas. Para Latin 1 y el tiempo de ejecución proporcionado por Oracle (11), esto parece funcionar. Así que proporcioné información adicional y eliminé mi comentario y voto negativo. Espero que esté bien para usted, de lo contrario, retroceda.
Maarten Bodewes
0

¡Esta es mi forma de hacerlo!

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

Caracteristicas :

  • Usar varargs (... ) para ser llamado con cualquier número de byte [].
  • Uso System.arraycopy()que se implementa con código nativo específico de la máquina, para garantizar un funcionamiento a alta velocidad.
  • Cree un nuevo byte [] con el tamaño exacto que lo necesita.
  • Asigne poco menos intvariables reutilizando las variables iy len.
  • Comparación más rápida con constantes.

Tener en cuenta :

La mejor manera de hacer esto es copiando el código @Jonathan . El problema proviene de las matrices de variables nativas, porque Java crea nuevas variables cuando este tipo de datos se pasa a otra función.

Daniel De León
fuente
1
No, esa es la forma en que Wayne lo hace , llegas 5 años tarde.
Maarten Bodewes
@MaartenBodewes Gracias a ti, utilizo tu comentario para ejercitar la codificación hoy, ahora es más diferente y con un mejor rendimiento.
Daniel De León
1
No estoy seguro de que importará demasiado, ya que los tamaños de matriz tampoco cambian en el tiempo de ejecución, pero ahora es diferente al menos de la otra solución.
Maarten Bodewes