Łatwy sposób na połączenie dwóch bajtów

249

Jak łatwo połączyć dwie bytetablice?

Mówić,

byte a[];
byte b[];

Jak połączyć dwie bytetablice i przechowywać je w innej bytetablicy?

androidGuy
źródło
3
Zauważ proszę, że Apache Commons, Google guawa System.arrayCopy, ByteBuffera część - nie tak skuteczne, ale czytelny - ByteArrayOutputStreamwszystkie zostały pokryte. Mamy ponad 7 duplikatów podanych tutaj odpowiedzi. Nie publikuj więcej duplikatów.
Maarten Bodewes

Odpowiedzi:

317

Najprostszy:

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
źródło
377

Najbardziej eleganckim sposobem na to jest użycie ByteArrayOutputStream.

byte a[];
byte b[];

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

byte c[] = outputStream.toByteArray( );
Kevin
źródło
61
@vipw Powodem, dla którego jest to eleganckie, jest to, że jeśli / jeśli chcesz później połączyć trzecią tablicę, po prostu dodajesz linię outputStream.write( c );- nie musisz cofać się i edytować linii, w której utworzono wynikową tablicę bajtów. Również zmiana kolejności tablic jest prosta, w przeciwieństwie do metody arraycopy.
Wayne Uroda
2
Ponadto jest to o wiele łatwiejsze w przypadku pracy z więcej niż tylko 2 bajtowymi tablicami.
gardarh 17.04.13
3
To, czy marnuje procesor i pamięć, zależy od częstotliwości wykonywania operacji. Jeśli to miliard razy na sekundę - z pewnością zoptymalizuj to. W przeciwnym razie zwycięską kwestią mogą być czytelność i łatwość konserwacji.
vikingsteve,
5
Jeśli problem dotyczy zużycia i / lub wydajności pamięci, użyj a.length + b.lengthargumentu dla ByteArrayOutputStreamkonstruktora. Zauważ, że ta metoda nadal skopiuje wszystkie bajty do nowej tablicy, aby ją przypisać c[]! Rozważ ByteBuffermetodę bliską rywalkę, która nie marnuje pamięci.
Maarten Bodewes
Naprawdę nie mogę dać temu kciuka w górę, ponieważ jest to tylko fragment kodu. Nie ma tutaj wyjaśnienia podstawowych elementów, które są tym, na czym mi zależy (i myślę, że większość ludzi by to zrobiła). Z chęcią podałbym to kciuki do góry, gdyby istniało porównanie wydajności pomiędzy System # arrayCopy (Object, int, Object, int, int) i ByteArrayOutputStream # put (byte []), a także szczegółowo, który scenariusz jest najlepszy dla obu opcji. To powiedziawszy, odpowiedź powinna również obejmować arrayCopy, ponieważ jest to inne rozwiązanie.
searchengine27
66

Oto rozwiązanie ładny użyciu Guava „s com.google.common.primitives.Bytes:

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

Wspaniałą rzeczą w tej metodzie jest to, że ma podpis varargs:

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

co oznacza, że ​​możesz połączyć dowolną liczbę tablic w jednym wywołaniu metody.

Zoltán
źródło
30

Inną możliwością jest użycie java.nio.ByteBuffer.

Coś jak

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

Zauważ, że tablica musi być odpowiednio dobrana na początek, więc wymagana jest linia alokacji (ponieważ array()po prostu zwraca tablicę bazową, bez uwzględnienia przesunięcia, położenia lub ograniczenia).

kalefranz
źródło
3
@click_whir Przepraszamy, ale ReadTheDocs. ByteBuffer.allocate(int)jest metodą statyczną, która zwraca instancję java.nio.HeapByteBuffer, podklasę ByteBuffer. Zajmuje się metodami .put()i .compact()- i każdą inną abstrakcyjnością.
kalefranz
@kalefranz Usunięto compact()linię, ponieważ jest niepoprawna.
Maarten Bodewes,
1
Zachowaj ostrożność podczas korzystania z metody ByteBuffer array () - chyba że absolutnie wiesz, co robisz, a łatwość konserwacji nie stanowi problemu, nie ma gwarancji, że pozycja zeroeth w buforze bajtowym zawsze odpowiada indeksowi 0 tablicy bajtów. Zobacz tutaj . Rozwiązuję to, wydając bb.flip(); bb.get(result);zamiast byte[] result = bb.array();wiersza.
DarqueSandu
1
@DarqueSandu Mimo, że ogólnie jest to dobra rada , uważna lektura allocatemetody ujawnia następujące informacje: „Pozycja nowego bufora będzie wynosić zero, jego limitem będzie pojemność, jego znak będzie niezdefiniowany, a każdy jego element zostanie zainicjowany na zero . Będzie miał tablicę podkładową, a przesunięcie tablicy będzie wynosić zero. Dlatego w przypadku tego konkretnego fragmentu kodu, w którym ByteBufferjest on przydzielany wewnętrznie, nie stanowi to problemu.
Maarten Bodewes,
13

Innym sposobem jest użycie funkcji narzędzia (jeśli chcesz, możesz to uczynić statyczną metodą ogólnej klasy narzędzi):

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

Wywołaj tak:

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

Będzie również działał dla łączenia 3, 4, 5 tablic itp.

Robiąc to w ten sposób, zyskujesz przewagę szybkiego kodu arraycopy, który jest również bardzo łatwy do odczytania i utrzymania.

Wayne Uroda
źródło
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
źródło
Ta sama odpowiedź co zaakceptowana i przepraszam, 5 minut spóźnienia.
Maarten Bodewes
11

Jeśli wolisz ByteBuffer@kalefranz, zawsze istnieje możliwość połączenia dwóch byte[](lub nawet więcej) w jednym wierszu, tak jak to:

byte[] c = ByteBuffer.allocate(a.length+b.length).put(a).put(b).array();
Zeus
źródło
Ta sama odpowiedź jak ta, ale ponad rok później. Stosuje łańcuchy metod, ale lepiej byłoby umieścić je w istniejącej odpowiedzi.
Maarten Bodewes
11

Możesz używać bibliotek stron trzecich do czyszczenia kodu, takich jak Apache Commons Lang i używać go w następujący sposób:

byte[] bytes = ArrayUtils.addAll(a, b);
Tomasz Przybylski
źródło
1
Próbowałem ArrayUtils.addAll(a, b)i byte[] c = Bytes.concat(a, b), ale ten drugi jest szybszy.
Carlos Andrés García
Może. Nie znam biblioteki Guava, więc jeśli tak, to lepiej z niej skorzystać. Czy sprawdziłeś to pod kątem bardzo dużych tablic?
Tomasz Przybylski
1
Kiedy zrobiłem test, tablica Firtsa miała 68 elementów długości i drugą długość 8790688.
Carlos Andrés García
5

W przypadku dwóch lub wielu tablic można zastosować tę prostą i czystą metodę narzędzia:

/**
 * 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
źródło
1
To marnuje pamięć. Ta metoda byłaby odpowiednia dla dwóch mniejszych tablic, ale z pewnością obciąży śmietnik za więcej tablic.
Maarten Bodewes
1

Scal dwie tablice bajtów PDF

Jeśli scalasz dwie tablice bajtów zawierające PDF, ta logika nie będzie działać. Musimy użyć narzędzia innej firmy, takiego jak PDFbox firmy 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.
źródło
trochę nie na temat tego pytania, ale właśnie tego szukałem.
amos
1

Jeśli nie chcesz zadzierać z rozmiarami tablic, po prostu użyj magii łączenia łańcuchów:

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

Lub zdefiniuj gdzieś w swoim kodzie

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

I użyć

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

To oczywiście działa również z więcej niż dwoma łączeniami łańcuchowymi za pomocą +operatora dodawania.


Oba "l1"i ISO_8859_1wskazują zestaw znaków Western Latin 1, który koduje każdy znak jako pojedynczy bajt. Ponieważ nie są wykonywane tłumaczenia wielobajtowe, znaki w ciągu będą miały takie same wartości jak bajty (z tym wyjątkiem, że zawsze będą interpretowane jako wartości dodatnie, ponieważ nie charsą podpisane). Przynajmniej w przypadku środowiska wykonawczego dostarczonego przez Oracle każdy bajt zostanie zatem poprawnie „zdekodowany”, a następnie „ponownie zakodowany”.

Uwaga: ciągi znacznie rozszerzają tablicę bajtów, co wymaga dodatkowej pamięci. Łańcuchy mogą być również internowane i dlatego nie będą łatwe do usunięcia. Ciągi są również niezmienne, więc wartości wewnątrz nich nie można zniszczyć. Dlatego nie powinieneś łączyć wrażliwych tablic w ten sposób, ani nie powinieneś używać tej metody dla tablic o większych bajtach. Wymagane byłoby również wyraźne wskazanie tego, co robisz, ponieważ ta metoda konkatenacji macierzy nie jest powszechnym rozwiązaniem.

John McClane
źródło
@ MaartenBodewes Jeśli nie masz pewności co do „l1” (który jest tylko aliasem dla ISO 8859-1), nie używaj słowa „na pewno”. Która konkretna wartość bajtu zostanie usunięta? Jeśli chodzi o wykorzystanie pamięci, pytanie dotyczyło łatwego sposobu połączenia dwóch bajtów, a nie najbardziej wydajnej pamięci.
John McClane
1
Złożyłem ostrzeżenia i przeprowadziłem testy. Wydaje się, że w przypadku Latin 1 i środowiska uruchomieniowego Oracle (11) to działa. Podałem więc dodatkowe informacje i usunąłem swój komentarz i głosowałem. Mam nadzieję, że Ci to odpowiada, w przeciwnym razie wycofaj się.
Maarten Bodewes,
0

To jest mój sposób, aby to zrobić!

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

Funkcje :

  • Użyj varargs ( ...), aby wywołać dowolną liczbę bajtów [].
  • Użyj, System.arraycopy()które jest zaimplementowane z natywnym kodem specyficznym dla maszyny, aby zapewnić szybkie działanie.
  • Utwórz nowy bajt [] z dokładnym wymaganym rozmiarem.
  • Przydziel niewiele mniej intzmiennych, ponownie wykorzystując zmienne ii len.
  • Szybsze porównanie ze stałymi.

Pamiętaj :

Lepszym sposobem na to jest skopiowanie kodu @Jonathan . Problem pochodzi z rodzimych tablic zmiennych, ponieważ Java tworzy nowe zmienne, gdy ten typ danych jest przekazywany do innej funkcji.

Daniel De León
źródło
1
Nie, to sposób Wayne'a, spóźniasz się 5 lat.
Maarten Bodewes
@MaartenBodewes Dzięki tobie wykorzystuję twój komentarz do ćwiczenia kodowania dzisiaj, teraz jest bardziej inny i ma lepszą wydajność.
Daniel De León
1
Nie jestem pewien, czy to będzie miało zbyt duże znaczenie, ponieważ rozmiary tablic również nie zmieniają się w środowisku wykonawczym, ale teraz różnią się przynajmniej od innych rozwiązań.
Maarten Bodewes