Maneira fácil de concatenar matrizes de dois bytes

249

Qual é a maneira mais fácil de concatenar duas bytematrizes?

Dizer,

byte a[];
byte b[];

Como concatenar duas bytematrizes e armazená-las em outra bytematriz?

androidGuy
fonte
3
Observe por favor que o Apache Commons, goiaba, do Google, System.arrayCopy, ByteBuffere a - não tão eficiente, mas legível - ByteArrayOutputStreamforam todos cobertos. Temos mais de 7 enganos das respostas dadas aqui. Por favor, não poste mais bobagens.
Maarten Bodewes

Respostas:

317

Mais simples:

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
fonte
377

A maneira mais elegante de fazer isso é com a ByteArrayOutputStream.

byte a[];
byte b[];

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

byte c[] = outputStream.toByteArray( );
Kevin
fonte
61
@vipw A razão pela qual isso é elegante é porque se / quando você deseja concatenar uma terceira matriz posteriormente, basta adicionar a linha outputStream.write( c );- você não precisa voltar e editar a linha em que cria a matriz de bytes resultante. Além disso, reordenar as matrizes é simples, ao contrário do método arraycopy.
Wayne Uroda 27/08/2012
2
Além disso, isso é muito mais fácil quando se trabalha com mais do que apenas matrizes de 2 bytes.
precisa saber é o seguinte
3
O desperdício de CPU e memória depende da frequência com que você faz a operação. Se é um bilhão de vezes por segundo, com certeza, otimize-o. Caso contrário, a legibilidade e a manutenção podem ser as considerações vencedoras.
vikingsteve
5
Se o consumo e / ou desempenho de memória for uma preocupação, use a.length + b.lengthcomo argumento o ByteArrayOutputStreamconstrutor. Observe que esse método ainda copiará todos os bytes para uma nova matriz à qual atribuir c[]! Considere o ByteBuffermétodo como um concorrente próximo, que não desperdiça memória.
Maarten Bodewes
Eu realmente não posso dar um joinha, porque este é apenas um trecho de código. Não há explicação para as peças subjacentes aqui, qual é a parte com a qual me preocupo (e acho que a maioria das pessoas faria). Eu ficaria feliz em dar um joinha se houvesse uma comparação de desempenho entre o System # arrayCopy (Object, int, Object, int, int) e ByteArrayOutputStream # put (byte []) e detalhando qual cenário é melhor para as duas opções. Além disso, a resposta também deve incluir arrayCopy, já que essa é outra solução.
searchengine27
66

Aqui está uma boa solução usando goiaba 's com.google.common.primitives.Bytes:

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

O melhor deste método é que ele possui uma assinatura varargs:

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

o que significa que você pode concatenar um número arbitrário de matrizes em uma única chamada de método.

Zoltán
fonte
30

Outra possibilidade está usando 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();

Observe que a matriz deve ser dimensionada adequadamente para começar, portanto a linha de alocação é necessária (como array()simplesmente retorna a matriz de suporte, sem levar em consideração o deslocamento, a posição ou o limite).

kalefranz
fonte
3
@click_whir Desculpe cara, mas ReadTheDocs. ByteBuffer.allocate(int)é um método estático que retorna uma java.nio.HeapByteBuffersubclasse instanciada ByteBuffer. Os métodos .put()e .compact()- e qualquer outra abstração - são resolvidos.
precisa saber é o seguinte
@kalefranz compact()Linha removida por estar incorreta.
Maarten Bodewes
1
Tome cuidado ao usar o método array () do ByteBuffer - a menos que você saiba absolutamente o que está fazendo e a manutenção não seja um problema, não há garantias de que a posição zero no bytebuffer corresponda sempre ao índice 0 da matriz de bytes. Veja aqui . Eu resolvo isso emitindo bb.flip(); bb.get(result);em vez da byte[] result = bb.array();linha.
DarqueSandu 11/05
1
@DarqueSandu Embora esse seja um bom conselho em geral , a leitura cuidadosa do allocatemétodo revela o seguinte: "A posição do novo buffer será zero, seu limite será sua capacidade, sua marca será indefinida e cada um de seus elementos será inicializado como zero Ele terá uma matriz de apoio e seu deslocamento da matriz será zero. " Portanto, para este determinado pedaço de código, onde o ByteBufferé alocada internamente, não é um problema.
Maarten Bodewes
13

Outra maneira é usar uma função de utilitário (você pode fazer disso um método estático de uma classe de utilitário genérica, se quiser):

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

Invoque assim:

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

Também funcionará para concatenar 3, 4, 5 matrizes etc.

Fazer dessa maneira oferece a vantagem do código rápido de cópia de matriz, que também é muito fácil de ler e manter.

Wayne Uroda
fonte
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
fonte
Mesma resposta que a aceita e desculpe, com 5 minutos de atraso.
Maarten Bodewes
11

Se você preferir ByteBuffercomo @kalefranz, sempre há a possibilidade de concatenar duas byte[](ou até mais) em uma linha, assim:

byte[] c = ByteBuffer.allocate(a.length+b.length).put(a).put(b).array();
Zeus
fonte
Mesma resposta que esta, mas com mais de um ano de atraso. Usa o encadeamento de métodos, mas isso seria melhor colocado na resposta existente.
Maarten Bodewes
11

Você pode usar bibliotecas de terceiros para o Código Limpo como o Apache Commons Lang e usá-lo como:

byte[] bytes = ArrayUtils.addAll(a, b);
Tomasz Przybylski
fonte
1
Eu tentei ArrayUtils.addAll(a, b)e byte[] c = Bytes.concat(a, b), mas o último é mais rápido.
Carlos Andrés García
Talvez. Eu não conheço a biblioteca do Guava, por isso, se for, é melhor usá-lo. Você verificou se há matrizes muito grandes?
Tomasz Przybylski
1
Quando fiz o teste, o array The Firts tinha 68 elementos de comprimento e o segundo comprimento de 8790688.
Carlos Andrés García
5

Para duas ou várias matrizes, este método utilitário simples e limpo pode ser usado:

/**
 * 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
fonte
1
Isso desperdiça memória. O método seria bom para duas matrizes menores, mas certamente tributará o coletor de lixo por mais matrizes.
Maarten Bodewes
1

Mesclar duas matrizes de bytes em PDF

Se você estiver mesclando matrizes de dois bytes que contêm PDF, essa lógica não funcionará. Precisamos usar uma ferramenta de terceiros como o PDFbox do 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
fonte
um pouco fora do tópico desta pergunta, mas é exatamente o que eu estava procurando.
22617 amos
1

Se você não quiser mexer no tamanho das matrizes, use a mágica da concatenação de strings:

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

Ou defina algum lugar no seu código

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

E use

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

Obviamente, isso também funciona com mais de duas concatenações de cadeias usando o +operador de adição.


Ambos "l1"e ISO_8859_1indicam o conjunto de caracteres 1 em latim ocidental que codifica cada caractere como um único byte. Como nenhuma conversão de vários bytes é executada, os caracteres na sequência terão os mesmos valores que os bytes (exceto que eles sempre serão interpretados como valores positivos, como charnão são assinados). Pelo menos para o tempo de execução fornecido pelo Oracle, qualquer byte será corretamente "decodificado" e depois "codificado" novamente.

Cuidado que as strings expandem consideravelmente a matriz de bytes, exigindo memória adicional. As strings também podem ser internadas e, portanto, não serão fáceis de remover. As strings também são imutáveis, portanto, os valores dentro delas não podem ser destruídos. Portanto, você não deve concatenar matrizes confidenciais dessa maneira nem usar esse método para matrizes de bytes maiores. Também é necessário fornecer uma indicação clara do que você está fazendo, pois esse método de concatenação de matriz não é uma solução comum.

John McClane
fonte
@MaartenBodewes Se você não tem certeza sobre "l1" (que é apenas um apelido para a ISO 8859-1), não use a palavra "certamente". Qual valor de byte específico será apagado? Quanto ao uso de memória, a questão era sobre a maneira mais fácil de concatenar matrizes de dois bytes, e não sobre a mais eficiente em termos de memória.
John McClane
1
Anotei alguns avisos e fiz alguns testes. Para o Latin 1 e o Oracle fornecido em tempo de execução (11), isso parece funcionar. Então, eu forneci informações extras e removi meu comentário e voto negativo. Espero que esteja bem para você, caso contrário, volte.
Maarten Bodewes
0

Esta é a minha maneira de fazê-lo!

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

Características :

  • Use varargs ( ...) para ser chamado com qualquer número de bytes [].
  • Use System.arraycopy()implementado com código nativo específico da máquina, para garantir a operação em alta velocidade.
  • Crie um novo byte [] com o tamanho exato necessário.
  • Aloque pouco menos intvariáveis, reutilizando as variáveis ie len.
  • Comparação mais rápida com constantes.

Tenha em mente :

A melhor maneira de fazer isso é copiando o código @Jonathan . O problema vem das matrizes de variáveis ​​nativas, porque o Java cria novas variáveis ​​quando esse tipo de dados é passado para outra função.

Daniel De León
fonte
1
Não, é o jeito de Wayne fazer isso , você está 5 anos atrasado.
Maarten Bodewes
@MaartenBodewes Graças a você, uso seu comentário para exercitar a codificação hoje, agora é mais diferente e com melhor desempenho.
Daniel De León
1
Não tenho certeza se isso importará muito, visto que os tamanhos de matriz também não mudam no tempo de execução, mas agora é diferente, pelo menos, da outra solução.
Maarten Bodewes