Quelles sont précisément vos exigences? Prenez-vous l'union des tableaux ou conservez-vous plusieurs instances de la même valeur? Voulez-vous que les articles soient triés ou souhaitez-vous conserver l'ordre dans les tableaux initiaux? Vous recherchez l'efficacité en vitesse ou en lignes de code?
Jason
J'adore, "le meilleur" dépend de vos besoins.
Ady
7
Si vous êtes capable d'utiliser LINQ, alors vous pouvez simplement utiliser la Concatméthode:IEnumerable<byte> arrays = array1.Concat(array2).Concat(array3);
casperOne
1
Veuillez essayer d'être plus clair dans vos questions. Cette vague question a causé beaucoup de confusion parmi ces personnes assez bonnes pour prendre le temps de vous répondre.
J'ai chronométré chacune des méthodes suggérées dans une boucle exécutée 1 million de fois en utilisant 3 tableaux de 10 octets chacun. Voici les résultats:
Nouveau tableau d'octets utilisant System.Array.Copy - 0,2187556 secondes
Nouveau tableau d'octets utilisant System.Buffer.BlockCopy - 0,1406286 secondes
IEnumerable <byte> utilisant l'opérateur de rendement C # - 0,0781270 secondes
IEnumerable <byte> utilisant Concat <> de LINQ - 0,0781270 secondes
J'ai augmenté la taille de chaque tableau à 100 éléments et relancé le test:
Nouveau tableau d'octets utilisant System.Array.Copy - 0,2812554 secondes
Nouveau tableau d'octets utilisant System.Buffer.BlockCopy - 0,2500048 secondes
IEnumerable <byte> utilisant l'opérateur de rendement C # - 0,0625012 secondes
IEnumerable <byte> utilisant Concat <> de LINQ - 0,0781265 secondes
J'ai augmenté la taille de chaque tableau à 1000 éléments et relancé le test:
Nouveau tableau d'octets utilisant System.Array.Copy - 1.0781457 secondes
Nouveau tableau d'octets utilisant System.Buffer.BlockCopy - 1.0156445 secondes
IEnumerable <byte> utilisant l'opérateur de rendement C # - 0,0625012 secondes
IEnumerable <byte> utilisant Concat <> de LINQ - 0,0781265 secondes
Enfin, j'ai augmenté la taille de chaque tableau à 1 million d'éléments et relancé le test, exécutant chaque boucle seulement 4000 fois:
Nouveau tableau d'octets utilisant System.Array.Copy - 13.4533833 secondes
Nouveau tableau d'octets utilisant System.Buffer.BlockCopy - 13.1096267 secondes
IEnumerable <byte> utilisant l'opérateur de rendement C # - 0 seconde
IEnumerable <byte> utilisant Concat <> de LINQ - 0 secondes
Donc, si vous avez besoin d'un nouveau tableau d'octets, utilisez
Mais, si vous pouvez utiliser une IEnumerable<byte>, préférez DEFINITIVEMENT la méthode Concat <> de LINQ. Il n'est que légèrement plus lent que l'opérateur de rendement C #, mais il est plus concis et plus élégant.
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
Si vous avez un nombre arbitraire de tableaux et utilisez .NET 3.5, vous pouvez rendre la System.Buffer.BlockCopysolution plus générique comme ceci:
* Remarque: Le bloc ci-dessus nécessite que vous ajoutiez l'espace de noms suivant en haut pour qu'il fonctionne.
using System.Linq;
Au point de Jon Skeet concernant l'itération des structures de données suivantes (tableau d'octets vs IEnumerable <byte>), j'ai réexécuté le dernier test de synchronisation (1 million d'éléments, 4000 itérations), en ajoutant une boucle qui itère sur le tableau complet avec chaque passer:
Nouveau tableau d'octets utilisant System.Array.Copy - 78.20550510 secondes
Nouveau tableau d'octets utilisant System.Buffer.BlockCopy - 77.89261900 secondes
IEnumerable <byte> utilisant l'opérateur de rendement C # - 551.7150161 secondes
IEnumerable <byte> utilisant Concat <> de LINQ - 448.1804799 secondes
Le fait est qu'il est TRÈS important de comprendre l'efficacité de la création et de l'utilisation de la structure de données résultante. Se concentrer simplement sur l'efficacité de la création peut ignorer l'inefficacité associée à l'utilisation. Bravo, Jon.
Mais êtes-vous en train de le convertir en un tableau à la fin, comme l'exige la question? Sinon, bien sûr, c'est plus rapide - mais cela ne répond pas aux exigences.
Jon Skeet
18
Re: Matt Davis - Peu importe si vos "exigences" doivent transformer l'IEnumerable en un tableau - tout ce dont vos exigences ont besoin, c'est que le résultat soit réellement utilisé dans une certaine mode . La raison pour laquelle vos tests de performances sur IEnumerable sont si faibles est que vous ne faites rien ! LINQ n'effectue aucun de ses travaux tant que vous n'essayez pas d'utiliser les résultats. Pour cette raison, je trouve que votre réponse est objectivement incorrecte et pourrait conduire d'autres à utiliser LINQ alors qu'ils ne devraient absolument pas le faire s'ils se soucient des performances.
csauve
12
J'ai lu la réponse entière, y compris votre mise à jour, mon commentaire est le même. Je sais que je rejoins le parti en retard, mais la réponse est extrêmement trompeuse et la première moitié est manifestement fausse .
csauve
14
Pourquoi la réponse qui contient des informations fausses et trompeuses est-elle la réponse la plus votée, et a été modifiée pour invalider complètement sa déclaration d'origine après que quelqu'un (Jon Skeet) a souligné qu'elle n'avait même pas répondu à la question OP?
MrCC
3
Réponse trompeuse. Même l'édition ne répond pas à la question.
Serge Profafilecebook
154
Beaucoup de réponses me semblent ignorer les exigences énoncées:
Le résultat doit être un tableau d'octets
Il doit être aussi efficace que possible
Ensemble, ces deux éléments excluent une séquence d'octets LINQ - tout ce qui yieldcontient rendra impossible l'obtention de la taille finale sans parcourir toute la séquence.
Si ce ne sont pas les vraies exigences bien sûr, LINQ pourrait être une bonne solution (ou l' IList<T>implémentation). Cependant, je suppose que Superdumbell sait ce qu'il veut.
(EDIT: je viens d'avoir une autre pensée. Il y a une grande différence sémantique entre faire une copie des tableaux et les lire paresseusement. Réfléchissez à ce qui se passe si vous modifiez les données dans l'un des tableaux "source" après avoir appelé le Combine(ou quoi que ce soit) ) mais avant d'utiliser le résultat - avec une évaluation paresseuse, ce changement sera visible. Avec une copie immédiate, il ne le sera pas. Différentes situations nécessiteront un comportement différent - juste quelque chose à savoir.)
Voici mes méthodes proposées - qui sont très similaires à celles contenues dans certaines des autres réponses, certainement :)
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;}
Bien sûr, la version "params" nécessite de créer d'abord un tableau des tableaux d'octets, ce qui introduit une inefficacité supplémentaire.
Jon, je comprends précisément ce que tu dis. Mon seul point est que parfois des questions sont posées avec une implémentation particulière déjà en tête sans se rendre compte que d'autres solutions existent. Fournir simplement une réponse sans proposer d'alternatives me semble un mauvais service. Pensées?
Matt Davis
1
@Matt: Oui, offrir des alternatives est une bonne chose - mais cela vaut la peine d'expliquer qu'il s'agit d' alternatives plutôt que de les faire passer pour la réponse à la question posée. (Je ne dis pas que vous l'avez fait - votre réponse est très bonne.)
Jon Skeet
4
(Bien que je pense que votre référence de performance devrait également montrer le temps nécessaire pour parcourir tous les résultats dans chaque cas, afin d'éviter de donner un avantage injuste à l'évaluation paresseuse.)
Jon Skeet
1
Même sans satisfaire à l'exigence de "résultat doit être un tableau", le simple fait de répondre à une exigence de "résultat doit être utilisé dans une certaine tendance" rendrait LINQ non optimal. Je pense que l'exigence de pouvoir utiliser le résultat devrait être implicite!
csauve
2
@andleer: Mis à part toute autre chose, Buffer.BlockCopy ne fonctionne qu'avec des types primitifs.
Jon Skeet
44
J'ai pris l'exemple LINQ de Matt un peu plus loin pour la propreté du code:
byte[] rv = a1.Concat(a2).Concat(a3).ToArray();
Dans mon cas, les tableaux sont petits, donc je ne suis pas préoccupé par les performances.
Solution courte et simple, un test de performance serait super!
Sebastian
3
C'est clairement clair, lisible, ne nécessite aucune bibliothèque / aide externe et, en termes de temps de développement, est assez efficace. Idéal lorsque les performances d'exécution ne sont pas critiques.
binki
28
Si vous avez simplement besoin d'un nouveau tableau d'octets, utilisez ce qui suit:
Alternativement, si vous avez juste besoin d'un seul IEnumerable, envisagez d'utiliser l'opérateur de rendement 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;}
J'ai fait quelque chose de similaire à votre 2e option pour fusionner de grands flux, fonctionnait comme un charme. :)
Greg D
2
La deuxième option est excellente. +1.
R. Martinho Fernandes
11
J'ai en fait rencontré des problèmes avec Concat ... (avec des tableaux dans les 10 millions, il s'est en fait écrasé).
J'ai trouvé que ce qui suit était simple, facile et fonctionne assez bien sans tomber en panne sur moi, et cela fonctionne pour N'IMPORTE QUEL nombre de tableaux (pas seulement trois) (Il utilise LINQ):
Comme l'a déclaré qwe, j'ai fait un test dans une boucle 10000000 fois, et MemoryStream est sorti 290% plus lent que Buffer.BlockCopy
esac
Dans certains cas, vous pouvez effectuer une itération sur un nombre énuméré de tableaux sans aucune connaissance préalable des longueurs de tableau individuelles. Cela fonctionne bien dans ce scénario. BlockCopy s'appuie sur un tableau de destination précréé
Sentinel
Comme l'a dit @Sentinel, cette réponse est parfaite pour moi car je n'ai aucune connaissance de la taille des choses que je dois écrire et me permet de faire les choses très proprement. Il joue également bien avec [ReadOnly] Span <byte> de .NET Core 3!
Water
Si vous initialisez MemoryStream avec la taille finale de la taille, il ne sera pas recréé et ce sera plus rapide @esac.
Malheureusement, cela ne fonctionnera pas avec tous les types. Marshal.SizeOf () ne pourra pas retourner une taille pour de nombreux types (essayez d'utiliser cette méthode avec des tableaux de chaînes et vous verrez une exception "Type 'System.String' ne peut pas être marshalé comme une structure non gérée; aucune taille significative ou le décalage peut être calculé ". Vous pouvez essayer de limiter le paramètre type aux types de référence uniquement (en ajoutant where T : struct), mais - n'étant pas un expert dans les entrailles du CLR - je ne pourrais pas dire si vous pourriez également obtenir des exceptions sur certaines structures (par exemple, s'ils contiennent des champs de type référence)
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();}}
Votre réponse pourrait être meilleure si vous aviez posté une petite explication sur ce que fait cet exemple de code.
AFract
1
il concatène un tableau de tableaux d'octets en un grand tableau d'octets (comme celui-ci): [1,2,3] + [4,5] + [6,7] ==> [1,2,3,4,5 , 6,7]
Peter Ertl
1
Peut utiliser des génériques pour combiner des tableaux. Le code suivant peut facilement être étendu à trois tableaux. De cette façon, vous n'avez jamais besoin de dupliquer le code pour différents types de tableaux. Certaines des réponses ci-dessus me semblent trop complexes.
Voici une généralisation de la réponse fournie par @Jon Skeet. C'est fondamentalement le même, seulement il est utilisable pour tout type de tableau, pas seulement les octets:
DANGER! Ces méthodes ne fonctionneront pas avec n'importe quel type de tableau avec des éléments de plus d'un octet (à peu près tout autre que les tableaux d'octets). Buffer.BlockCopy () fonctionne avec des quantités d'octets, pas des nombres d'éléments de tableau. La raison pour laquelle il peut être utilisé facilement avec un tableau d'octets est que chaque élément du tableau est un seul octet, donc la longueur physique du tableau est égale au nombre d'éléments. Pour transformer les méthodes byte [] de John en méthodes génériques, vous devrez multiplier tous les décalages et longueurs par la longueur en octets d'un seul élément du tableau - sinon vous ne copierez pas toutes les données.
Daniel Scott
2
Normalement, pour que cela fonctionne, vous devez calculer la taille d'un seul élément en utilisant sizeof(...)et multiplier cela par le nombre d'éléments que vous souhaitez copier, mais sizeof ne peut pas être utilisé avec un type générique. Il est possible - pour certains types - d'utiliser Marshal.SizeOf(typeof(T)), mais vous obtiendrez des erreurs d'exécution avec certains types (par exemple des chaînes). Quelqu'un avec une connaissance plus approfondie du fonctionnement interne des types CLR sera en mesure de signaler tous les pièges possibles ici. Autant dire que l'écriture d'une méthode de concaténation de tableau générique [à l'aide de BlockCopy] n'est pas anodine.
Daniel Scott
2
Et enfin - vous pouvez écrire une méthode de concaténation de tableau générique comme celle-ci presque exactement de la manière indiquée ci-dessus (avec des performances légèrement inférieures) en utilisant Array.Copy à la place. Remplacez simplement tous les appels Buffer.BlockCopy par des appels Array.Copy.
Merci pour votre contribution. Puisqu'il existe déjà un certain nombre de réponses très appréciées à ce sujet il y a plus de dix ans, il serait utile de fournir une explication de ce qui distingue votre approche. Pourquoi quelqu'un devrait-il utiliser cela au lieu par exemple de la réponse acceptée?
Jeremy Caney
J'aime utiliser des méthodes étendues, car il y a un code clair à comprendre. Ce code sélectionne deux tableaux avec un index de départ et un nombre et une concaténation. Cette méthode s'est également étendue. Donc, c'est pour tous les types de tableaux prêts à tout moment
Mehmet ÜNLÜ
Cela me semble logique! Pourriez-vous modifier votre question pour y inclure ces informations? Je pense qu'il serait utile pour les futurs lecteurs de disposer de cette information dès le départ, afin qu'ils puissent rapidement distinguer votre approche des réponses existantes. Je vous remercie!
Jeremy Caney
-1
Tout ce dont vous avez besoin pour passer la liste des tableaux d'octets et cette fonction vous renverra le tableau d'octets (fusionné). C'est la meilleure solution, je pense :).
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 est la bonne réponse, mais pour une raison quelconque, une chose contrôlée manuellement obtient le plus de votes. Si vous aimez cette réponse, peut-être aimeriez-vous encore plus cette solution plus générale:
IEnumerable<byte>Combine(paramsbyte[][] arrays){foreach(byte[] a in arrays)foreach(byte b in a)yieldreturn b;}
qui vous permettrait de faire des choses comme:
byte[] c =Combine(newbyte[]{0,1,2},newbyte[]{3,4,5}).ToArray();
La question demande spécifiquement la solution la plus efficace . Enumerable.ToArray ne va pas être très efficace, car il ne peut pas connaître la taille du tableau final pour commencer - contrairement aux techniques roulées à la main.
Concat
méthode:IEnumerable<byte> arrays = array1.Concat(array2).Concat(array3);
Réponses:
Pour les types primitifs (y compris les octets), utilisez
System.Buffer.BlockCopy
plutôt queSystem.Array.Copy
. C'est plus rapide.J'ai chronométré chacune des méthodes suggérées dans une boucle exécutée 1 million de fois en utilisant 3 tableaux de 10 octets chacun. Voici les résultats:
System.Array.Copy
- 0,2187556 secondesSystem.Buffer.BlockCopy
- 0,1406286 secondesJ'ai augmenté la taille de chaque tableau à 100 éléments et relancé le test:
System.Array.Copy
- 0,2812554 secondesSystem.Buffer.BlockCopy
- 0,2500048 secondesJ'ai augmenté la taille de chaque tableau à 1000 éléments et relancé le test:
System.Array.Copy
- 1.0781457 secondesSystem.Buffer.BlockCopy
- 1.0156445 secondesEnfin, j'ai augmenté la taille de chaque tableau à 1 million d'éléments et relancé le test, exécutant chaque boucle seulement 4000 fois:
System.Array.Copy
- 13.4533833 secondesSystem.Buffer.BlockCopy
- 13.1096267 secondesDonc, si vous avez besoin d'un nouveau tableau d'octets, utilisez
Mais, si vous pouvez utiliser une
IEnumerable<byte>
, préférez DEFINITIVEMENT la méthode Concat <> de LINQ. Il n'est que légèrement plus lent que l'opérateur de rendement C #, mais il est plus concis et plus élégant.Si vous avez un nombre arbitraire de tableaux et utilisez .NET 3.5, vous pouvez rendre la
System.Buffer.BlockCopy
solution plus générique comme ceci:* Remarque: Le bloc ci-dessus nécessite que vous ajoutiez l'espace de noms suivant en haut pour qu'il fonctionne.
Au point de Jon Skeet concernant l'itération des structures de données suivantes (tableau d'octets vs IEnumerable <byte>), j'ai réexécuté le dernier test de synchronisation (1 million d'éléments, 4000 itérations), en ajoutant une boucle qui itère sur le tableau complet avec chaque passer:
System.Array.Copy
- 78.20550510 secondesSystem.Buffer.BlockCopy
- 77.89261900 secondesLe fait est qu'il est TRÈS important de comprendre l'efficacité de la création et de l'utilisation de la structure de données résultante. Se concentrer simplement sur l'efficacité de la création peut ignorer l'inefficacité associée à l'utilisation. Bravo, Jon.
la source
Beaucoup de réponses me semblent ignorer les exigences énoncées:
Ensemble, ces deux éléments excluent une séquence d'octets LINQ - tout ce qui
yield
contient rendra impossible l'obtention de la taille finale sans parcourir toute la séquence.Si ce ne sont pas les vraies exigences bien sûr, LINQ pourrait être une bonne solution (ou l'
IList<T>
implémentation). Cependant, je suppose que Superdumbell sait ce qu'il veut.(EDIT: je viens d'avoir une autre pensée. Il y a une grande différence sémantique entre faire une copie des tableaux et les lire paresseusement. Réfléchissez à ce qui se passe si vous modifiez les données dans l'un des tableaux "source" après avoir appelé le
Combine
(ou quoi que ce soit) ) mais avant d'utiliser le résultat - avec une évaluation paresseuse, ce changement sera visible. Avec une copie immédiate, il ne le sera pas. Différentes situations nécessiteront un comportement différent - juste quelque chose à savoir.)Voici mes méthodes proposées - qui sont très similaires à celles contenues dans certaines des autres réponses, certainement :)
Bien sûr, la version "params" nécessite de créer d'abord un tableau des tableaux d'octets, ce qui introduit une inefficacité supplémentaire.
la source
J'ai pris l'exemple LINQ de Matt un peu plus loin pour la propreté du code:
Dans mon cas, les tableaux sont petits, donc je ne suis pas préoccupé par les performances.
la source
Si vous avez simplement besoin d'un nouveau tableau d'octets, utilisez ce qui suit:
Alternativement, si vous avez juste besoin d'un seul IEnumerable, envisagez d'utiliser l'opérateur de rendement C # 2.0:
la source
J'ai en fait rencontré des problèmes avec Concat ... (avec des tableaux dans les 10 millions, il s'est en fait écrasé).
J'ai trouvé que ce qui suit était simple, facile et fonctionne assez bien sans tomber en panne sur moi, et cela fonctionne pour N'IMPORTE QUEL nombre de tableaux (pas seulement trois) (Il utilise LINQ):
la source
La classe memorystream fait très bien ce travail pour moi. Je n'ai pas pu faire fonctionner la classe tampon aussi vite que memorystream.
la source
la source
where T : struct
), mais - n'étant pas un expert dans les entrailles du CLR - je ne pourrais pas dire si vous pourriez également obtenir des exceptions sur certaines structures (par exemple, s'ils contiennent des champs de type référence)la source
Peut utiliser des génériques pour combiner des tableaux. Le code suivant peut facilement être étendu à trois tableaux. De cette façon, vous n'avez jamais besoin de dupliquer le code pour différents types de tableaux. Certaines des réponses ci-dessus me semblent trop complexes.
la source
Voici une généralisation de la réponse fournie par @Jon Skeet. C'est fondamentalement le même, seulement il est utilisable pour tout type de tableau, pas seulement les octets:
la source
sizeof(...)
et multiplier cela par le nombre d'éléments que vous souhaitez copier, mais sizeof ne peut pas être utilisé avec un type générique. Il est possible - pour certains types - d'utiliserMarshal.SizeOf(typeof(T))
, mais vous obtiendrez des erreurs d'exécution avec certains types (par exemple des chaînes). Quelqu'un avec une connaissance plus approfondie du fonctionnement interne des types CLR sera en mesure de signaler tous les pièges possibles ici. Autant dire que l'écriture d'une méthode de concaténation de tableau générique [à l'aide de BlockCopy] n'est pas anodine.la source
Tout ce dont vous avez besoin pour passer la liste des tableaux d'octets et cette fonction vous renverra le tableau d'octets (fusionné). C'est la meilleure solution, je pense :).
la source
Concat est la bonne réponse, mais pour une raison quelconque, une chose contrôlée manuellement obtient le plus de votes. Si vous aimez cette réponse, peut-être aimeriez-vous encore plus cette solution plus générale:
qui vous permettrait de faire des choses comme:
la source