Quais são especificamente os seus requisitos? Você está adotando a união das matrizes ou está preservando várias instâncias do mesmo valor? Deseja classificar os itens ou preservar a ordem nas matrizes iniciais? Você está procurando eficiência em velocidade ou em linhas de código?
jason
Adore, "o melhor" depende de quais são suas necessidades.
213 Ady
7
Se você é capaz de usar LINQ, então você pode simplesmente usar o Concatmétodo:IEnumerable<byte> arrays = array1.Concat(array2).Concat(array3);
casperOne
1
Por favor, tente ser mais claro em suas perguntas. Essa vaga pergunta causou muita confusão entre as pessoas boas o suficiente para reservar um tempo para responder a você.
Mas, se você puder usar um IEnumerable<byte>, DEFINITIVAMENTE prefira o método Concat <> do LINQ. É apenas um pouco mais lento que o operador de rendimento C #, mas é mais conciso e mais elegante.
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
Se você tiver um número arbitrário de matrizes e estiver usando o .NET 3.5, poderá tornar a System.Buffer.BlockCopysolução mais genérica como esta:
* Nota: O bloco acima requer a adição do seguinte espaço para nome na parte superior para que ele funcione.
using System.Linq;
Para o argumento de Jon Skeet em relação à iteração das estruturas de dados subsequentes (matriz de bytes vs. IEnumerable <byte>), executei novamente o último teste de temporização (1 milhão de elementos, 4000 iterações), adicionando um loop que itera sobre a matriz completa a cada passar:
Nova matriz de bytes usando System.Array.Copy - 78.20550510 segundos
Nova matriz de bytes usando System.Buffer.BlockCopy - 77.89261900 segundos
IEnumerable <byte> usando o operador de rendimento C # - 551.7150161 segundos
IEnumerable <byte> usando Concat do LINQ <> - 448.1804799 segundos
O ponto é que é MUITO importante entender a eficiência da criação e do uso da estrutura de dados resultante. O simples foco na eficiência da criação pode ignorar a ineficiência associada ao uso. Parabéns, Jon.
Mas você está realmente convertendo-o em uma matriz no final, conforme a pergunta exige? Caso contrário, é claro que é mais rápido - mas não está cumprindo os requisitos.
Jon Skeet
18
Re: Matt Davis - Não importa se seus "requisitos" precisam transformar o IEnumerable em uma matriz - tudo o que seus requisitos precisam é que o resultado seja realmente usado em alguma fase . A razão pela qual seus testes de desempenho no IEnumerable são tão baixos é porque você não está realmente fazendo nada ! O LINQ não executa nenhum de seus trabalhos até você tentar usar os resultados. Por esse motivo, acho sua resposta objetivamente incorreta e poderia levar outras pessoas a usar o LINQ quando não deveriam, se não se importassem com o desempenho.
Csauve
12
Eu li toda a resposta, incluindo sua atualização, meu comentário permanece. Sei que estou entrando na festa tarde, mas a resposta é muito enganadora e a primeira metade é claramente falsa .
Csauve
14
Por que a resposta que contém informações falsas e enganosas é a resposta mais votada e foi editada para invalidar completamente sua declaração original depois que alguém (Jon Skeet) apontou que nem sequer respondeu à pergunta do OP?
MrCC
3
Resposta enganosa. Até a edição não está respondendo à pergunta.
Serge Profafilecebook
154
Muitas das respostas me parecem ignorar os requisitos declarados:
O resultado deve ser uma matriz de bytes
Deve ser o mais eficiente possível
Esses dois juntos descartam uma sequência de bytes LINQ - qualquer coisa com yieldisso tornará impossível obter o tamanho final sem iterar por toda a sequência.
Se esses não são os requisitos reais , é claro, o LINQ poderia ser uma solução perfeitamente boa (ou a IList<T>implementação). No entanto, vou assumir que Superdumbell sabe o que quer.
(EDIT: Acabei de ter outro pensamento. Há uma grande diferença semântica entre fazer uma cópia das matrizes e lê-las preguiçosamente. Considere o que acontece se você alterar os dados em uma das matrizes "de origem" depois de chamar o Combine(ou o que seja ), mas antes de usar o resultado - com uma avaliação lenta, essa alteração será visível. Com uma cópia imediata, não. Situações diferentes exigirão um comportamento diferente - apenas algo para estar ciente.)
Aqui estão meus métodos propostos - que são muito semelhantes aos contidos em algumas das outras respostas, certamente :)
publicstaticbyte[]Combine(byte[] first,byte[] second){byte[] ret =newbyte[first.Length+ second.Length];Buffer.BlockCopy(first,0, ret,0, first.Length);Buffer.BlockCopy(second,0, ret, first.Length, second.Length);return ret;}publicstaticbyte[]Combine(byte[] first,byte[] second,byte[] third){byte[] ret =newbyte[first.Length+ second.Length+ third.Length];Buffer.BlockCopy(first,0, ret,0, first.Length);Buffer.BlockCopy(second,0, ret, first.Length, second.Length);Buffer.BlockCopy(third,0, ret, first.Length+ second.Length,
third.Length);return ret;}publicstaticbyte[]Combine(paramsbyte[][] arrays){byte[] ret =newbyte[arrays.Sum(x => x.Length)];int offset =0;foreach(byte[] data in arrays){Buffer.BlockCopy(data,0, ret, offset, data.Length);
offset += data.Length;}return ret;}
Obviamente, a versão "params" requer a criação de uma matriz de matrizes de bytes primeiro, o que introduz ineficiência extra.
Jon, eu entendo exatamente o que você está dizendo. Meu único argumento é que algumas vezes as perguntas são feitas com uma implementação específica já em mente, sem perceber que existem outras soluções. Simplesmente fornecer uma resposta sem oferecer alternativas me parece um desserviço. Pensamentos?
Matt Davis
1
@ Matt: Sim, oferecer alternativas é bom - mas vale a pena explicar que elas são alternativas, em vez de passar adiante como resposta à pergunta que está sendo feita. (Eu não estou dizendo que você fez isso -. A sua resposta é muito bom)
Jon Skeet
4
(Embora eu acho que sua avaliação de desempenho deve mostrar o tempo necessário para percorrer todos os resultados em cada caso, também, para evitar dar avaliação preguiçosa uma vantagem injusta.)
Jon Skeet
1
Mesmo sem atender ao requisito de "resultado deve ser uma matriz", simplesmente atender a um requisito de "resultado deve ser usado em algum momento" tornaria o LINQ não ideal. Eu acho que esse requisito para poder usar o resultado deve estar implícito!
Csauve
2
@andleer: Além de qualquer outra coisa, o Buffer.BlockCopy funciona apenas com tipos primitivos.
Jon Skeet
44
Levei o exemplo de LINQ de Matt um passo adiante para a limpeza do código:
byte[] rv = a1.Concat(a2).Concat(a3).ToArray();
No meu caso, as matrizes são pequenas, então não estou preocupado com o desempenho.
Solução curta e simples, um teste de desempenho seria ótimo!
Sebastian
3
Isso é definitivamente claro, legível, não requer bibliotecas / auxiliares externos e, em termos de tempo de desenvolvimento, é bastante eficiente. Ótimo quando o desempenho em tempo de execução não é crítico.
precisa saber é
28
Se você simplesmente precisar de uma nova matriz de bytes, use o seguinte:
Como alternativa, se você precisar apenas de um único IEnumerable, considere usar o operador de rendimento C # 2.0:
IEnumerable<byte>Combine(byte[] a1,byte[] a2,byte[] a3){foreach(byte b in a1)yieldreturn b;foreach(byte b in a2)yieldreturn b;foreach(byte b in a3)yieldreturn b;}
Fiz algo semelhante à sua segunda opção para mesclar fluxos grandes, funcionou como um encanto. :)
Greg D
2
A segunda opção é ótima. +1.
R. Martinho Fernandes
10
Na verdade, eu tive alguns problemas com o uso do Concat ... (com matrizes nos 10 milhões, ele realmente travou).
Achei o seguinte simples, fácil e funciona bem o suficiente sem ter de travar comigo, e funciona para QUALQUER número de matrizes (não apenas três) (ele usa LINQ):
Como qwe afirmou, fiz um teste em loop 10.000.000 de vezes, e o MemoryStream saiu 290% MAIS LENTO que o Buffer
esac
Em alguns casos, você pode estar iterando sobre um número incontável de matrizes sem nenhum conhecimento prévio dos comprimentos individuais da matriz. Isso funciona bem nesse cenário. BlockCopy depende de ter uma matriz de destino precreated
Sentinela
Como o @Sentinel disse, esta resposta é perfeita para mim porque não tenho conhecimento do tamanho das coisas que tenho que escrever e me permite fazer as coisas com muita clareza. Também funciona bem com o [ReadOnly] Span <byte> do .NET Core 3!
Água
Se você inicializar o MemoryStream com o tamanho final do tamanho, ele não será recriado e será mais rápido @esac.
Infelizmente, isso não funcionará com todos os tipos. Marshal.SizeOf () não poderá retornar um tamanho para muitos tipos (tente usar esse método com matrizes de seqüências de caracteres e você verá uma exceção "O tipo 'System.String' não pode ser empacotado como uma estrutura não gerenciada; nenhum tamanho significativo ou offset pode ser calculado ". Você poderia tentar limitar o parâmetro type apenas aos tipos de referência (adicionando where T : struct), mas - não sendo um especialista nas entranhas do CLR - não sabia dizer se também poderia haver exceções em certas estruturas (por exemplo, se eles contêm campos de tipo de referência).
Daniel Scott
2
publicstaticbyte[]Concat(paramsbyte[][] arrays){
using (var mem =newMemoryStream(arrays.Sum(a => a.Length))){foreach(vararrayin arrays){
mem.Write(array,0,array.Length);}return mem.ToArray();}}
Sua resposta poderia ser melhor se você tivesse postado uma pequena explicação sobre o que esse código de exemplo.
AFract
1
concatena uma matriz de matrizes de bytes em uma matriz de bytes grande (como esta): [1,2,3] + [4,5] + [6,7] ==> [1,2,3,4,5 , 6,7]
Peter Ertl
1
Pode usar genéricos para combinar matrizes. O código a seguir pode ser facilmente expandido para três matrizes. Dessa forma, você nunca precisará duplicar o código para diferentes tipos de matrizes. Algumas das respostas acima parecem muito complexas para mim.
Aqui está uma generalização da resposta fornecida por @Jon Skeet. É basicamente o mesmo, mas é utilizável para qualquer tipo de matriz, não apenas bytes:
PERIGO! Esses métodos não funcionarão com nenhum tipo de matriz com elementos maiores que um byte (praticamente tudo que não sejam matrizes de bytes). Buffer.BlockCopy () trabalha com quantidades de bytes, não com números de elementos da matriz. O motivo pelo qual ele pode ser usado facilmente com uma matriz de bytes é que cada elemento da matriz é um único byte; portanto, o comprimento físico da matriz é igual ao número de elementos. Para transformar os métodos de byte [] de John em métodos genéricos, você precisará multiplicar todos os deslocamentos e comprimentos pelo comprimento de bytes de um único elemento da matriz - caso contrário, você não copiará todos os dados.
Daniel Scott
2
Normalmente, para fazer isso funcionar, você calcula o tamanho de um único elemento usando sizeof(...)e multiplica pelo número de elementos que deseja copiar, mas sizeof não pode ser usado com um tipo genérico. É possível - para alguns tipos - usar Marshal.SizeOf(typeof(T)), mas você obterá erros de tempo de execução com certos tipos (por exemplo, strings). Alguém com um conhecimento mais profundo do funcionamento interno dos tipos de CLR poderá apontar todas as possíveis armadilhas aqui. Basta dizer que escrever um método genérico de concatenação de matriz [usando BlockCopy] não é trivial.
Daniel Scott
2
E finalmente - você pode escrever um método genérico de concatenação de matrizes como esse quase da maneira mostrada acima (com desempenho ligeiramente inferior) usando Array.Copy. Apenas substitua todas as chamadas Buffer.BlockCopy por chamadas Array.Copy.
Obrigado pela contribuição. Como já existem várias respostas altamente classificadas para isso de mais de uma década atrás, seria útil oferecer uma explicação sobre o que distingue sua abordagem. Por que alguém deveria usar isso em vez de, por exemplo, a resposta aceita?
Jeremy Caney
Eu gosto de usar métodos estendidos, porque há um código claro para entender. Este código seleciona duas matrizes com índice inicial, contagem e concat. Este método também foi estendido. Portanto, isso é para todos os tipos de matriz prontos para todos os tempos
Mehmet ÜNLÜ
Isso faz sentido para mim! Você se importa de editar sua pergunta para incluir essas informações? Eu acho que seria valioso para os futuros leitores ter isso com antecedência, para que eles possam distinguir rapidamente sua abordagem das respostas existentes. Obrigado!
Jeremy Caney
-1
Tudo o que você precisa para passar na lista de matrizes de bytes e essa função retornará a matriz de bytes (mesclada). Esta é a melhor solução que eu acho :).
publicstaticbyte[]CombineMultipleByteArrays(List<byte[]> lstByteArray){
using (var ms =newMemoryStream()){
using (var doc =new iTextSharp.text.Document()){
using (var copy =newPdfSmartCopy(doc, ms)){
doc.Open();foreach(var p in lstByteArray){
using (var reader =newPdfReader(p)){
copy.AddDocument(reader);}}
doc.Close();}}return ms.ToArray();}}
Concat é a resposta certa, mas, por alguma razão, uma coisa de mão está recebendo mais votos. Se você gosta dessa resposta, talvez queira essa solução mais geral ainda mais:
IEnumerable<byte>Combine(paramsbyte[][] arrays){foreach(byte[] a in arrays)foreach(byte b in a)yieldreturn b;}
o que permitiria fazer coisas como:
byte[] c =Combine(newbyte[]{0,1,2},newbyte[]{3,4,5}).ToArray();
A pergunta pede especificamente a solução mais eficiente . Enumerable.ToArray não será muito eficiente, pois não pode saber o tamanho da matriz final para começar - ao passo que as técnicas feitas manualmente.
Concat
método:IEnumerable<byte> arrays = array1.Concat(array2).Concat(array3);
Respostas:
Para tipos primitivos (incluindo bytes), use em
System.Buffer.BlockCopy
vez deSystem.Array.Copy
. É mais rápido.Cronometrei cada um dos métodos sugeridos em um loop executado 1 milhão de vezes usando 3 matrizes de 10 bytes cada. Aqui estão os resultados:
System.Array.Copy
- 0.2187556 segundosSystem.Buffer.BlockCopy
- 0,1406286 segundosAumentei o tamanho de cada matriz para 100 elementos e refiz o teste:
System.Array.Copy
- 0,2812554 segundosSystem.Buffer.BlockCopy
- 0,2500048 segundosAumentei o tamanho de cada matriz para 1000 elementos e refiz o teste:
System.Array.Copy
- 1.0781457 segundosSystem.Buffer.BlockCopy
- 1.0156445 segundosPor fim, aumentei o tamanho de cada matriz para 1 milhão de elementos e refiz o teste, executando cada loop apenas 4000 vezes:
System.Array.Copy
- 13.4533833 segundosSystem.Buffer.BlockCopy
- 13.1096267 segundosPortanto, se você precisar de uma nova matriz de bytes, use
Mas, se você puder usar um
IEnumerable<byte>
, DEFINITIVAMENTE prefira o método Concat <> do LINQ. É apenas um pouco mais lento que o operador de rendimento C #, mas é mais conciso e mais elegante.Se você tiver um número arbitrário de matrizes e estiver usando o .NET 3.5, poderá tornar a
System.Buffer.BlockCopy
solução mais genérica como esta:* Nota: O bloco acima requer a adição do seguinte espaço para nome na parte superior para que ele funcione.
Para o argumento de Jon Skeet em relação à iteração das estruturas de dados subsequentes (matriz de bytes vs. IEnumerable <byte>), executei novamente o último teste de temporização (1 milhão de elementos, 4000 iterações), adicionando um loop que itera sobre a matriz completa a cada passar:
System.Array.Copy
- 78.20550510 segundosSystem.Buffer.BlockCopy
- 77.89261900 segundosO ponto é que é MUITO importante entender a eficiência da criação e do uso da estrutura de dados resultante. O simples foco na eficiência da criação pode ignorar a ineficiência associada ao uso. Parabéns, Jon.
fonte
Muitas das respostas me parecem ignorar os requisitos declarados:
Esses dois juntos descartam uma sequência de bytes LINQ - qualquer coisa com
yield
isso tornará impossível obter o tamanho final sem iterar por toda a sequência.Se esses não são os requisitos reais , é claro, o LINQ poderia ser uma solução perfeitamente boa (ou a
IList<T>
implementação). No entanto, vou assumir que Superdumbell sabe o que quer.(EDIT: Acabei de ter outro pensamento. Há uma grande diferença semântica entre fazer uma cópia das matrizes e lê-las preguiçosamente. Considere o que acontece se você alterar os dados em uma das matrizes "de origem" depois de chamar o
Combine
(ou o que seja ), mas antes de usar o resultado - com uma avaliação lenta, essa alteração será visível. Com uma cópia imediata, não. Situações diferentes exigirão um comportamento diferente - apenas algo para estar ciente.)Aqui estão meus métodos propostos - que são muito semelhantes aos contidos em algumas das outras respostas, certamente :)
Obviamente, a versão "params" requer a criação de uma matriz de matrizes de bytes primeiro, o que introduz ineficiência extra.
fonte
Levei o exemplo de LINQ de Matt um passo adiante para a limpeza do código:
No meu caso, as matrizes são pequenas, então não estou preocupado com o desempenho.
fonte
Se você simplesmente precisar de uma nova matriz de bytes, use o seguinte:
Como alternativa, se você precisar apenas de um único IEnumerable, considere usar o operador de rendimento C # 2.0:
fonte
Na verdade, eu tive alguns problemas com o uso do Concat ... (com matrizes nos 10 milhões, ele realmente travou).
Achei o seguinte simples, fácil e funciona bem o suficiente sem ter de travar comigo, e funciona para QUALQUER número de matrizes (não apenas três) (ele usa LINQ):
fonte
A classe memorystream faz esse trabalho muito bem para mim. Não consegui que a classe de buffer funcionasse tão rápido quanto o fluxo de memória.
fonte
fonte
where T : struct
), mas - não sendo um especialista nas entranhas do CLR - não sabia dizer se também poderia haver exceções em certas estruturas (por exemplo, se eles contêm campos de tipo de referência).fonte
Pode usar genéricos para combinar matrizes. O código a seguir pode ser facilmente expandido para três matrizes. Dessa forma, você nunca precisará duplicar o código para diferentes tipos de matrizes. Algumas das respostas acima parecem muito complexas para mim.
fonte
Aqui está uma generalização da resposta fornecida por @Jon Skeet. É basicamente o mesmo, mas é utilizável para qualquer tipo de matriz, não apenas bytes:
fonte
sizeof(...)
e multiplica pelo número de elementos que deseja copiar, mas sizeof não pode ser usado com um tipo genérico. É possível - para alguns tipos - usarMarshal.SizeOf(typeof(T))
, mas você obterá erros de tempo de execução com certos tipos (por exemplo, strings). Alguém com um conhecimento mais profundo do funcionamento interno dos tipos de CLR poderá apontar todas as possíveis armadilhas aqui. Basta dizer que escrever um método genérico de concatenação de matriz [usando BlockCopy] não é trivial.fonte
Tudo o que você precisa para passar na lista de matrizes de bytes e essa função retornará a matriz de bytes (mesclada). Esta é a melhor solução que eu acho :).
fonte
Concat é a resposta certa, mas, por alguma razão, uma coisa de mão está recebendo mais votos. Se você gosta dessa resposta, talvez queira essa solução mais geral ainda mais:
o que permitiria fazer coisas como:
fonte