Jakie konkretnie są twoje wymagania? Czy bierzesz połączenie tablic, czy zachowujesz wiele wystąpień o tej samej wartości? Czy chcesz posortować elementy, czy też zachować porządek w początkowych tablicach? Szukasz wydajności w szybkości lub w liniach kodu?
jason
Uwielbiam to, „najlepsze” zależy od twoich wymagań.
Ady
7
Jeśli możesz korzystać z LINQ, możesz po prostu użyć Concatmetody:IEnumerable<byte> arrays = array1.Concat(array2).Concat(array3);
casperOne
1
Postaraj się być bardziej przejrzysty w swoich pytaniach. To niejasne pytanie spowodowało wiele zamieszania wśród osób wystarczająco dobrych, aby poświęcić czas na udzielenie odpowiedzi.
Ale jeśli możesz użyć IEnumerable<byte>, ZDECYDOWANIE wolisz metodę Concat <> LINQ. Jest tylko nieco wolniejszy niż operator plonu C #, ale jest bardziej zwięzły i bardziej elegancki.
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
Jeśli masz dowolną liczbę tablic i korzystasz z .NET 3.5, możesz uczynić to System.Buffer.BlockCopyrozwiązanie bardziej ogólnym:
* Uwaga: Powyższy blok wymaga dodania następującej przestrzeni nazw u góry, aby działał.
using System.Linq;
Do punktu Jona Skeeta dotyczącego iteracji kolejnych struktur danych (tablica bajtów vs. IEnumerable <byte>) ponownie uruchomiłem ostatni test synchronizacji (1 milion elementów, 4000 iteracji), dodając pętlę, która iteruje pełną tablicę z każdym przechodzić:
Nowa tablica bajtów przy użyciu System.Array.Copy - 78,20550510 sekund
Nowa tablica bajtów przy użyciu System.Buffer.BlockCopy - 77,89261900 sekund
IEnumerable <byte> przy użyciu operatora C # - 551,7150161 sekund
IEnumerable <byte> przy użyciu Concat LINQ <> - 448.1804799 sekund
Chodzi o to, że BARDZO ważne jest zrozumienie wydajności zarówno tworzenia, jak i wykorzystania wynikowej struktury danych. Skupienie się na wydajności kreacji może przeoczyć nieefektywność związaną z użyciem. Kudos, Jon.
Ale czy na końcu konwertujesz go do tablicy, jak wymaga tego pytanie? Jeśli nie, to oczywiście szybciej - ale nie spełnia wymagań.
Jon Skeet
18
Re: Matt Davis - Nie ma znaczenia, czy twoje „wymagania” wymagają przekształcenia IEnumerable w tablicę - wszystko czego potrzebujesz, to fakt, że wynik jest faktycznie używany w jakiejś fasadzie . Powodem, dla którego twoje testy wydajności na IEnumerable są tak niskie, jest to, że tak naprawdę nic nie robisz ! LINQ nie wykonuje żadnej ze swoich prac, dopóki nie spróbujesz użyć wyników. Z tego powodu uważam twoją odpowiedź za obiektywnie niepoprawną i może skłonić innych do korzystania z LINQ, gdy absolutnie nie powinni, jeśli zależy im na wydajności.
csauve
12
Przeczytałem całą odpowiedź, w tym twoją aktualizację, mój komentarz jest ważny. Wiem, że dołączam do drużyny późno, ale odpowiedź jest rażąco myląca, a pierwsza połowa jest oczywiście fałszywa .
csauve
14
Dlaczego odpowiedź, która zawiera fałszywe i wprowadzające w błąd informacje, jest najczęściej głosowaną odpowiedzią i została zredagowana w celu całkowitego unieważnienia jej oryginalnego oświadczenia po tym, jak ktoś (Jon Skeet) zauważył, że nawet nie odpowiedział na pytanie OP?
MrCC
3
Myląca odpowiedź. Nawet wydanie nie odpowiada na pytanie.
Serge Profafilecebook
154
Wydaje mi się, że wiele odpowiedzi ignoruje podane wymagania:
Wynik powinien być tablicą bajtów
Powinien być tak wydajny, jak to możliwe
Te dwa razem wykluczają sekwencję bajtów LINQ - wszystko, yieldco może uniemożliwić uzyskanie ostatecznego rozmiaru bez iteracji przez całą sekwencję.
Jeśli to nie są prawdziwe wymagania, LINQ może być idealnie dobrym rozwiązaniem (lub IList<T>implementacją). Zakładam jednak, że Superdumbell wie, czego chce.
(EDYCJA: Właśnie pomyślałem. Jest duża różnica semantyczna między tworzeniem kopii tablic a leniwym ich czytaniem. Zastanów się, co się stanie, jeśli zmienisz dane w jednej z tablic „źródłowych” po wywołaniu Combine(lub cokolwiek innego) ), ale przed użyciem wyniku - przy leniwej ocenie, zmiana będzie widoczna. W przypadku natychmiastowej kopii nie będzie. Różne sytuacje będą wymagały innego zachowania - po prostu coś, o czym należy pamiętać.)
Oto moje proponowane metody - które są bardzo podobne do tych zawartych w niektórych innych odpowiedziach, na pewno :)
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;}
Oczywiście wersja „params” wymaga najpierw utworzenia tablicy tablic bajtów, co wprowadza dodatkową nieefektywność.
Jon, rozumiem dokładnie, co mówisz. Chodzi mi tylko o to, że czasami zadawane są pytania z myślą o konkretnej implementacji, nie zdając sobie sprawy z istnienia innych rozwiązań. Samo udzielenie odpowiedzi bez oferowania alternatyw wydaje mi się niekorzystne. Myśli?
Matt Davis
1
@Matt: Tak, oferowanie alternatyw jest dobre - ale warto wyjaśnić, że są one alternatywami, zamiast podawać je jako odpowiedź na zadawane pytanie. (Nie twierdzę, że to zrobiłeś - twoja odpowiedź jest bardzo dobra.)
Jon Skeet
4
(Chociaż myślę, że twój test wydajności powinien również pokazywać czas potrzebny na przejrzenie wszystkich wyników w każdym przypadku, aby uniknąć leniwej oceny nieuczciwej przewagi.)
Jon Skeet
1
Nawet bez spełnienia wymogu „wynik musi być tablicą”, samo spełnienie wymogu „wynik musi być użyty w jakimś fasionie” sprawiłoby, że LINQ nie byłby optymalny. Myślę, że wymóg, aby móc skorzystać z wyniku, powinien być dorozumiany!
csauve
2
@andleer: Oprócz czegokolwiek innego, Buffer.BlockCopy działa tylko z typami pierwotnymi.
Jon Skeet
44
Wziąłem przykład Matta o LINQ o krok dalej w kwestii czystości kodu:
byte[] rv = a1.Concat(a2).Concat(a3).ToArray();
W moim przypadku tablice są małe, więc nie martwię się o wydajność.
Krótkie i proste rozwiązanie, test wydajności byłby świetny!
Sebastian
3
Jest to zdecydowanie jasne, czytelne, nie wymaga zewnętrznych bibliotek / pomocników, a pod względem czasu programowania jest dość wydajne. Świetne, gdy wydajność w czasie wykonywania nie jest krytyczna.
binki
28
Jeśli potrzebujesz nowej tablicy bajtów, skorzystaj z następujących opcji:
Alternatywnie, jeśli potrzebujesz tylko jednego IEnumerable, rozważ użycie operatora wydajności 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;}
Zrobiłem coś podobnego do drugiej opcji łączenia dużych strumieni, działając jak urok. :)
Greg D
2
Druga opcja jest świetna. +1.
R. Martinho Fernandes
11
Właściwie napotkałem pewne problemy z używaniem Concat ... (z tablicami na 10 milionach, faktycznie się zawiesił).
Odkryłem, że poniższe są proste, łatwe i działają wystarczająco dobrze, nie powodując awarii, i działa na KAŻDEJ liczbie tablic (nie tylko trzech) (używa LINQ):
Jak powiedziałem qwe, zrobiłem test w pętli 10 000 000 razy, a MemoryStream wyszedł 290% wolniej niż bufor. BlockCopy
esac
W niektórych przypadkach możesz iterować po wyliczanej liczbie tablic bez uprzedniej wiedzy o poszczególnych długościach tablic. Działa to dobrze w tym scenariuszu. BlockCopy polega na przygotowaniu tablicy docelowej
Sentinel
Jak powiedział @Sentinel, ta odpowiedź jest dla mnie idealna, ponieważ nie mam wiedzy na temat wielkości rzeczy, które muszę pisać i pozwala mi robić wszystko bardzo czysto. Dobrze gra również w .NET Core 3 w [ReadOnly] Span <byte>!
Woda,
Jeśli zainicjujesz MemoryStream końcowym rozmiarem rozmiaru, nie zostanie on odtworzony i będzie szybszy @esac.
Niestety nie będzie to działać ze wszystkimi typami. Marshal.SizeOf () nie będzie w stanie zwrócić rozmiaru dla wielu typów (spróbuj użyć tej metody z tablicami łańcuchów, a zobaczysz wyjątek „Typ 'System.String” nie może być zestawiony jako struktura niezarządzana; brak znaczącego rozmiaru lub offset można obliczyć ". Możesz spróbować ograniczyć parametr typu tylko do typów referencyjnych (dodając where T : struct), ale - nie będąc ekspertem w wewnętrznych CLR - nie mogłem powiedzieć, czy możesz uzyskać wyjątki również dla niektórych struktur (np. jeśli zawierają pola typu odniesienia)
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();}}
Twoja odpowiedź mogłaby być lepsza, gdybyś zamieścił małe wyjaśnienie tego, co zawiera ten przykładowy kod.
AFract
1
konkatenuje tablicę bajtów w jedną dużą tablicę bajtów (tak jak to): [1,2,3] + [4,5] + [6,7] ==> [1,2,3,4,5 , 6,7]
Peter Ertl,
1
Może używać rodzajów ogólnych do łączenia tablic. Poniższy kod można łatwo rozszerzyć do trzech tablic. W ten sposób nigdy nie trzeba duplikować kodu dla różnych typów tablic. Niektóre z powyższych odpowiedzi wydają mi się zbyt skomplikowane.
ZAGROŻENIE! Te metody nie będą działać z właściwością dowolnego typu tablicy z elementami dłuższymi niż jeden bajt (prawie wszystko inne niż tablice bajtów). Buffer.BlockCopy () działa z ilością bajtów, a nie z liczbą elementów tablicy. Powodem, dla którego można go łatwo używać z tablicą bajtów, jest to, że każdy element tablicy jest jednym bajtem, więc fizyczna długość tablicy jest równa liczbie elementów. Aby zamienić metody bajtowe [] Johna na metody ogólne, musisz pomnożyć wszystkie przesunięcia i długości przez długość bajtu pojedynczego elementu tablicy - w przeciwnym razie nie skopiujesz wszystkich danych.
Daniel Scott
2
Zwykle, aby to zadziałało, należy obliczyć rozmiar pojedynczego elementu za pomocą sizeof(...)i pomnożyć go przez liczbę elementów, które chcesz skopiować, ale sizeof nie można używać z typem ogólnym. Możliwe jest - w przypadku niektórych typów - użycie Marshal.SizeOf(typeof(T)), ale w przypadku niektórych typów (np. Łańcuchów) pojawią się błędy w czasie wykonywania. Ktoś z bardziej dogłębną znajomością wewnętrznego działania typów CLR będzie w stanie wskazać tutaj wszystkie możliwe pułapki. Wystarczy powiedzieć, że napisanie ogólnej metody konkatenacji tablic [przy użyciu BlockCopy] nie jest trywialne.
Daniel Scott
2
I na koniec - możesz napisać taką ogólną metodę konkatenacji tablic w prawie dokładnie taki sposób, jak pokazano powyżej (z nieco niższą wydajnością), używając zamiast tego Array.Copy. Wystarczy zastąpić wszystkie wywołania Buffer.BlockCopy wywołaniami Array.Copy.
Dziękuję za wkład. Ponieważ istnieje już wiele wysoko ocenianych odpowiedzi na to pytanie ponad dziesięć lat temu, przydatne byłoby wyjaśnienie tego, co wyróżnia twoje podejście. Dlaczego ktoś miałby to wykorzystać zamiast np. Zaakceptowanej odpowiedzi?
Jeremy Caney
Lubię korzystać z rozszerzonych metod, ponieważ są jasne kody do zrozumienia. Kod ten wybiera dwie tablice z indeksem początkowym oraz zliczaniem i konkatacją. Również ta metoda została rozszerzona.
Dotyczy
To ma dla mnie sens! Czy masz coś przeciwko edycji pytania, aby uwzględnić te informacje? Myślę, że przydałoby się to z wyprzedzeniem czytelnikom, aby mogli szybko odróżnić twoje podejście od istniejących odpowiedzi. Dziękuję Ci!
Jeremy Caney
-1
Wystarczy przekazać listę tablic bajtów, a ta funkcja zwróci tablicę bajtów (scalone). Myślę, że to najlepsze rozwiązanie :).
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 jest właściwą odpowiedzią, ale z jakiegoś powodu najwięcej głosów zdobywa się na liście kontrolnej. Jeśli podoba ci się ta odpowiedź, być może chciałbyś jeszcze bardziej ogólne rozwiązanie:
IEnumerable<byte>Combine(paramsbyte[][] arrays){foreach(byte[] a in arrays)foreach(byte b in a)yieldreturn b;}
co pozwoli ci robić takie rzeczy jak:
byte[] c =Combine(newbyte[]{0,1,2},newbyte[]{3,4,5}).ToArray();
Pytanie dotyczy w szczególności najbardziej wydajnego rozwiązania. Enumerable.ToArray nie będzie bardzo wydajny, ponieważ nie może znać rozmiaru ostatecznej tablicy na początek - podczas gdy techniki zwijane ręcznie mogą.
Concat
metody:IEnumerable<byte> arrays = array1.Concat(array2).Concat(array3);
Odpowiedzi:
W przypadku typów pierwotnych (w tym bajtów) użyj
System.Buffer.BlockCopy
zamiastSystem.Array.Copy
. To jest szybsze.Ustawiłem każdą z sugerowanych metod w pętli wykonanej 1 milion razy, stosując 3 tablice po 10 bajtów każda. Oto wyniki:
System.Array.Copy
- 0,2187556 sekundSystem.Buffer.BlockCopy
- 0,1406286 sekundZwiększyłem rozmiar każdej tablicy do 100 elementów i ponownie uruchomiłem test:
System.Array.Copy
- 0,2812554 sekundSystem.Buffer.BlockCopy
- 0,2500048 sekundZwiększyłem rozmiar każdej tablicy do 1000 elementów i ponownie uruchomiłem test:
System.Array.Copy
- 1.0781457 sekundSystem.Buffer.BlockCopy
- 1.0156445 sekundNa koniec zwiększyłem rozmiar każdej tablicy do 1 miliona elementów i ponownie uruchomiłem test, wykonując każdą pętlę tylko 4000 razy:
System.Array.Copy
- 13,4533833 sekundSystem.Buffer.BlockCopy
- 13.1096267 sekundWięc jeśli potrzebujesz nowej tablicy bajtów, użyj
Ale jeśli możesz użyć
IEnumerable<byte>
, ZDECYDOWANIE wolisz metodę Concat <> LINQ. Jest tylko nieco wolniejszy niż operator plonu C #, ale jest bardziej zwięzły i bardziej elegancki.Jeśli masz dowolną liczbę tablic i korzystasz z .NET 3.5, możesz uczynić to
System.Buffer.BlockCopy
rozwiązanie bardziej ogólnym:* Uwaga: Powyższy blok wymaga dodania następującej przestrzeni nazw u góry, aby działał.
Do punktu Jona Skeeta dotyczącego iteracji kolejnych struktur danych (tablica bajtów vs. IEnumerable <byte>) ponownie uruchomiłem ostatni test synchronizacji (1 milion elementów, 4000 iteracji), dodając pętlę, która iteruje pełną tablicę z każdym przechodzić:
System.Array.Copy
- 78,20550510 sekundSystem.Buffer.BlockCopy
- 77,89261900 sekundChodzi o to, że BARDZO ważne jest zrozumienie wydajności zarówno tworzenia, jak i wykorzystania wynikowej struktury danych. Skupienie się na wydajności kreacji może przeoczyć nieefektywność związaną z użyciem. Kudos, Jon.
źródło
Wydaje mi się, że wiele odpowiedzi ignoruje podane wymagania:
Te dwa razem wykluczają sekwencję bajtów LINQ - wszystko,
yield
co może uniemożliwić uzyskanie ostatecznego rozmiaru bez iteracji przez całą sekwencję.Jeśli to nie są prawdziwe wymagania, LINQ może być idealnie dobrym rozwiązaniem (lub
IList<T>
implementacją). Zakładam jednak, że Superdumbell wie, czego chce.(EDYCJA: Właśnie pomyślałem. Jest duża różnica semantyczna między tworzeniem kopii tablic a leniwym ich czytaniem. Zastanów się, co się stanie, jeśli zmienisz dane w jednej z tablic „źródłowych” po wywołaniu
Combine
(lub cokolwiek innego) ), ale przed użyciem wyniku - przy leniwej ocenie, zmiana będzie widoczna. W przypadku natychmiastowej kopii nie będzie. Różne sytuacje będą wymagały innego zachowania - po prostu coś, o czym należy pamiętać.)Oto moje proponowane metody - które są bardzo podobne do tych zawartych w niektórych innych odpowiedziach, na pewno :)
Oczywiście wersja „params” wymaga najpierw utworzenia tablicy tablic bajtów, co wprowadza dodatkową nieefektywność.
źródło
Wziąłem przykład Matta o LINQ o krok dalej w kwestii czystości kodu:
W moim przypadku tablice są małe, więc nie martwię się o wydajność.
źródło
Jeśli potrzebujesz nowej tablicy bajtów, skorzystaj z następujących opcji:
Alternatywnie, jeśli potrzebujesz tylko jednego IEnumerable, rozważ użycie operatora wydajności C # 2.0:
źródło
Właściwie napotkałem pewne problemy z używaniem Concat ... (z tablicami na 10 milionach, faktycznie się zawiesił).
Odkryłem, że poniższe są proste, łatwe i działają wystarczająco dobrze, nie powodując awarii, i działa na KAŻDEJ liczbie tablic (nie tylko trzech) (używa LINQ):
źródło
Klasa memorystream wykonuje tę pracę dla mnie całkiem nieźle. Nie mogłem uruchomić klasy bufora tak szybko, jak memorystream.
źródło
źródło
where T : struct
), ale - nie będąc ekspertem w wewnętrznych CLR - nie mogłem powiedzieć, czy możesz uzyskać wyjątki również dla niektórych struktur (np. jeśli zawierają pola typu odniesienia)źródło
Może używać rodzajów ogólnych do łączenia tablic. Poniższy kod można łatwo rozszerzyć do trzech tablic. W ten sposób nigdy nie trzeba duplikować kodu dla różnych typów tablic. Niektóre z powyższych odpowiedzi wydają mi się zbyt skomplikowane.
źródło
Oto uogólnienie odpowiedzi udzielonej przez @Jona Skeeta. Jest w zasadzie taki sam, tylko można go używać do dowolnego typu tablicy, nie tylko bajtów:
źródło
sizeof(...)
i pomnożyć go przez liczbę elementów, które chcesz skopiować, ale sizeof nie można używać z typem ogólnym. Możliwe jest - w przypadku niektórych typów - użycieMarshal.SizeOf(typeof(T))
, ale w przypadku niektórych typów (np. Łańcuchów) pojawią się błędy w czasie wykonywania. Ktoś z bardziej dogłębną znajomością wewnętrznego działania typów CLR będzie w stanie wskazać tutaj wszystkie możliwe pułapki. Wystarczy powiedzieć, że napisanie ogólnej metody konkatenacji tablic [przy użyciu BlockCopy] nie jest trywialne.źródło
Wystarczy przekazać listę tablic bajtów, a ta funkcja zwróci tablicę bajtów (scalone). Myślę, że to najlepsze rozwiązanie :).
źródło
Concat jest właściwą odpowiedzią, ale z jakiegoś powodu najwięcej głosów zdobywa się na liście kontrolnej. Jeśli podoba ci się ta odpowiedź, być może chciałbyś jeszcze bardziej ogólne rozwiązanie:
co pozwoli ci robić takie rzeczy jak:
źródło