Jak mogę połączyć dwie tablice w Javie?

1366

Muszę połączyć dwie Stringtablice w Javie.

void f(String[] first, String[] second) {
    String[] both = ???
}

Jak najłatwiej to zrobić?

Antti Kissaniemi
źródło
3
Bytes.concat z Guava
Ben Page
1
Widzę tu wiele odpowiedzi, ale pytanie jest tak sformułowane („najłatwiejszy sposób”?), Że nie pozwala wskazać najlepszej odpowiedzi ...
Artur Opaliński
2
Dziesiątki odpowiedzi tutaj kopiują dane do nowej tablicy, ponieważ o to poproszono - ale kopiowanie danych, gdy nie jest to absolutnie konieczne, jest złe, szczególnie w Javie. Zamiast tego śledź indeksy i użyj dwóch tablic tak, jakby były połączone. Dodałem rozwiązanie ilustrujące technikę.
Douglas odbył się
12
Fakt, że takie pytanie ma obecnie 50 różnych odpowiedzi, sprawia, że ​​zastanawiam się, dlaczego Java nigdy nie uzyskała prostej array1 + array2konkatenacji.
JollyJoker
2
Możesz to zrobić doskonale dobrze i bardzo skutecznie w dwóch liniach standardowej Javy (patrz moja odpowiedź), więc nie ma wiele do zyskania, mając jedną metodę. Wszystkie te dziwne i cudowne rozwiązania to trochę strata czasu.
rghome

Odpowiedzi:

1093

Znalazłem jedno-liniowe rozwiązanie ze starej dobrej biblioteki Apache Commons Lang.
ArrayUtils.addAll(T[], T...)

Kod:

String[] both = ArrayUtils.addAll(first, second);
Antti Kissaniemi
źródło
175
Jak to jest „oszukiwać”, jeśli odpowiada na pytanie? Oczywiście, posiadanie dodatkowej zależności jest prawdopodobnie przesadą w tej konkretnej sytuacji, ale nie robi się nic złego w wołaniu, że ona istnieje, zwłaszcza że w Apache Commons jest tak wiele doskonałych funkcji.
Rob
33
Zgadzam się, to tak naprawdę nie odpowiada na pytanie. Biblioteki wysokiego poziomu mogą być świetne, ale jeśli chcesz dowiedzieć się, jak to zrobić skutecznie, przyjrzyj się kodowi używanemu przez bibliotekę. Ponadto w wielu sytuacjach nie można w locie przechodzić przez inną bibliotekę produktu.
AdamC
76
Myślę, że to dobra odpowiedź. Dostarczono również rozwiązania POJO, ale jeśli OP używa już Apache Commons w swoim programie (całkowicie możliwe, biorąc pod uwagę jego popularność), może nadal nie znać tego rozwiązania. Wtedy nie będzie „dodawał zależności dla tej jednej metody”, ale lepiej wykorzystywałby istniejącą bibliotekę.
Adam,
14
Jeśli zawsze martwisz się, że nie dodasz biblioteki dla jednej metody, żadne nowe biblioteki nigdy nie zostaną dodane. Biorąc pod uwagę doskonałe narzędzia dostępne w Apache Commons, bardzo polecam dodanie go, gdy pojawi się pierwszy przypadek użycia.
Hindol
6
używanie wspólnego apache nigdy nie powinno być nazywane „oszustwem”. Podważam rozsądek programistów, którzy uważają to za niepotrzebną zależność.
Jeryl Cook
768

Oto prosta metoda, która połączy dwie tablice i zwróci wynik:

public <T> T[] concatenate(T[] a, T[] b) {
    int aLen = a.length;
    int bLen = b.length;

    @SuppressWarnings("unchecked")
    T[] c = (T[]) Array.newInstance(a.getClass().getComponentType(), aLen + bLen);
    System.arraycopy(a, 0, c, 0, aLen);
    System.arraycopy(b, 0, c, aLen, bLen);

    return c;
}

Pamiętaj, że nie będzie działać z pierwotnymi typami danych, tylko z typami obiektów.

Poniższa nieco bardziej skomplikowana wersja działa zarówno z tablicami obiektowymi, jak i pierwotnymi. Robi to za pomocą Tzamiast T[]jako typu argumentu.

Umożliwia także łączenie tablic dwóch różnych typów poprzez wybranie najbardziej ogólnego typu jako typu komponentu wyniku.

public static <T> T concatenate(T a, T b) {
    if (!a.getClass().isArray() || !b.getClass().isArray()) {
        throw new IllegalArgumentException();
    }

    Class<?> resCompType;
    Class<?> aCompType = a.getClass().getComponentType();
    Class<?> bCompType = b.getClass().getComponentType();

    if (aCompType.isAssignableFrom(bCompType)) {
        resCompType = aCompType;
    } else if (bCompType.isAssignableFrom(aCompType)) {
        resCompType = bCompType;
    } else {
        throw new IllegalArgumentException();
    }

    int aLen = Array.getLength(a);
    int bLen = Array.getLength(b);

    @SuppressWarnings("unchecked")
    T result = (T) Array.newInstance(resCompType, aLen + bLen);
    System.arraycopy(a, 0, result, 0, aLen);
    System.arraycopy(b, 0, result, aLen, bLen);        

    return result;
}

Oto przykład:

Assert.assertArrayEquals(new int[] { 1, 2, 3 }, concatenate(new int[] { 1, 2 }, new int[] { 3 }));
Assert.assertArrayEquals(new Number[] { 1, 2, 3f }, concatenate(new Integer[] { 1, 2 }, new Number[] { 3f }));
Lii
źródło
1
Podoba mi się ta sugestia, ponieważ jest mniej zależna od najnowszych wersji Java. W moich projektach często utknąłem przy użyciu starszych wersji profili Java lub CLDC, w których niektóre funkcje, takie jak wymienione przez Antti, nie są dostępne.
kvn
4
Następująca linia przerwie część ogólną: concatenate (new String [] {"1"}, new Object [] {new Object ()})
dragon66
fajnie byłoby nie używać adnotacji @SuppressWarnings - poniżej opublikuję rozwiązanie tego problemu.
beaudet
+1 dla Array.newInstance(a.getClass().getComponentType(), aLen + bLen);. Zaskakująco nigdy wcześniej tego nie widziałem. @beaudet Myślę, że adnotacja jest tutaj dobra, biorąc pod uwagę, dlaczego jest tłumiona.
Blake
1
ha, nazywaj mnie purystą, ale wolę czysty kod, który nie wymaga tłumienia ostrzeżeń w celu usunięcia ostrzeżeń
beaudet
475

Możliwe jest napisanie w pełni ogólnej wersji, którą można nawet rozszerzyć, aby połączyć dowolną liczbę tablic. Te wersje wymagają Java 6, tak jak używająArrays.copyOf()

Obie wersje unikają tworzenia jakichkolwiek Listobiektów pośrednich i używają, System.arraycopy()aby zapewnić, że kopiowanie dużych tablic jest tak szybkie jak to możliwe.

W przypadku dwóch tablic wygląda to tak:

public static <T> T[] concat(T[] first, T[] second) {
  T[] result = Arrays.copyOf(first, first.length + second.length);
  System.arraycopy(second, 0, result, first.length, second.length);
  return result;
}

A dla dowolnej liczby tablic (> = 1) wygląda to tak:

public static <T> T[] concatAll(T[] first, T[]... rest) {
  int totalLength = first.length;
  for (T[] array : rest) {
    totalLength += array.length;
  }
  T[] result = Arrays.copyOf(first, totalLength);
  int offset = first.length;
  for (T[] array : rest) {
    System.arraycopy(array, 0, result, offset, array.length);
    offset += array.length;
  }
  return result;
}
Joachim Sauer
źródło
10
@ djBO: w przypadku tablic o typie prymitywnym konieczne byłoby przeciążenie każdego typu: po prostu skopiuj kod i zamień każdy Tz nich byte(i stracisz <T>).
Joachim Sauer
czy możesz mi powiedzieć, jak używać operatora typu <T> w mojej klasie?
Johnydep
6
Dodałbym to na początek, żeby być defensywnym. if (first == null) {if (second == null) {return null; } powrót drugi; } if (second == null) {return first; }
maraton
4
@djBo: co z:ByteBuffer buffer = ByteBuffer.allocate(array1.length + array2.length); buffer.put(array1); buffer.put(array2); return buffer.array();
Sam Goldberg,
18
W tym podejściu występuje błąd, który staje się widoczny, jeśli wywołujesz te funkcje z tablicami różnych typów komponentów, na przykład concat(ai, ad)gdzie aijest Integer[]i adjest Double[]. (W tym przypadku parametr Typ <T>zostanie rozwiązany <? extends Number>przez kompilator). Tablicy utworzonej przez Arrays.copyOfbędzie miał typu części pierwszej tablicy, to znaczy Integerw tym przykładzie. Gdy funkcja ma zamiar skopiować drugą tablicę, ArrayStoreExceptionzostanie wyrzucony znak an . Rozwiązaniem jest mieć dodatkowy Class<T> typeparametr.
T-Bull
457

Korzystanie Streamz Java 8:

String[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b))
                      .toArray(String[]::new);

Lub w ten sposób, używając flatMap:

String[] both = Stream.of(a, b).flatMap(Stream::of)
                      .toArray(String[]::new);

Aby to zrobić dla typu ogólnego, musisz użyć odbicia:

@SuppressWarnings("unchecked")
T[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b)).toArray(
    size -> (T[]) Array.newInstance(a.getClass().getComponentType(), size));
Lii
źródło
28
Jak to jest wydajne?
Supuhstar,
8
Warto przeczytać: jaxenter.com/... tl; dr - strumienie mogą być wydajne lub nie, zależy to od tego, co robisz z nimi i ograniczeń problemu (czy to nie zawsze jest odpowiedź? Lol)
Trevor Brown
6
Dodatkowo, jeśli a lub b są tablicami typów pierwotnych, ich strumienie będą musiały być .boxed()takie, aby były typu, Streama nie np. IntStreamKtóre nie mogą być przekazane jako parametr do Stream.concat.
Will Hardwick-Smith
17
@Will Hardwick-Smith: nie, musisz tylko wybrać odpowiednią klasę strumienia, np. Jeśli ai bint[], użyjint[] both = IntStream.concat(Arrays.stream(a), Arrays.stream(b)).toArray();
Holger
3
@Supuhstar: Prawdopodobnie nie jest tak szybki jak System.arrayCopy. Ale też niezbyt powolny. Prawdopodobnie masz zrobić to na bardzo wiele razy z ogromnych tablic w naprawdę wrażliwych wydajności kontekstów dla różnicy czasu wykonanie znaczenia.
Lii,
191

Lub z ukochaną Guawą :

String[] both = ObjectArrays.concat(first, second, String.class);

Istnieją również wersje prymitywnych tablic:

  • Booleans.concat(first, second)
  • Bytes.concat(first, second)
  • Chars.concat(first, second)
  • Doubles.concat(first, second)
  • Shorts.concat(first, second)
  • Ints.concat(first, second)
  • Longs.concat(first, second)
  • Floats.concat(first, second)
KARASZI István
źródło
Mimo że uwielbiam Guava, metoda z Apache Commons lepiej radzi sobie z nullabami.
Ravi Wallau
7
Chociaż dobrze jest korzystać z bibliotek, niefortunnie jest, że problem został usunięty. Dlatego podstawowe rozwiązanie pozostaje nieuchwytne.
user924272
51
W czym problem z abstrakcją? Nie wiem, o co tu chodzi z ponownym wynalezieniem koła, jeśli chcesz poznać problem, sprawdź źródło lub przeczytaj. Profesjonalny kod powinien korzystać z bibliotek wysokiego poziomu, znacznie lepiej, jeśli jest napisany w Google!
Breno Salgado,
@RaviWallau Czy możesz link do klasy, która to robi?
Sébastien Tromp
1
@ SébastienTromp Jest to najlepsze rozwiązanie dla tego pytania - ArrayUtils.
Ravi Wallau
70

Możesz dołączyć dwie tablice w dwóch wierszach kodu.

String[] both = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, both, first.length, second.length);

Jest to szybkie i wydajne rozwiązanie, które będzie działać na prymitywne typy, a obie metody są przeciążone.

Należy unikać rozwiązań obejmujących ArrayLists, strumienie itp., Ponieważ będą one musiały przydzielić pamięć tymczasową bez żadnego użytecznego celu.

Należy unikać forpętli dla dużych tablic, ponieważ nie są one wydajne. Wbudowane metody wykorzystują funkcje szybkiego kopiowania bloków.

rghome
źródło
1
To jedno z najlepszych rozwiązań. 100% standardowa Java. Szybki / wydajny. Powinny uzyskać więcej pozytywnych opinii!
Shebla Tsama
58

Korzystanie z Java API:

String[] f(String[] first, String[] second) {
    List<String> both = new ArrayList<String>(first.length + second.length);
    Collections.addAll(both, first);
    Collections.addAll(both, second);
    return both.toArray(new String[both.size()]);
}
Fabian Steeg
źródło
13
Po prostu, ale nieefektywnie, ponieważ tworzy tablicę dla ArrayList, a następnie generuje kolejną dla metody toArray. Ale nadal obowiązuje, ponieważ jest łatwy do odczytania.
PhoneixS
1
dotyczy ciągów i obiektów (zgodnie z pytaniem), ale nie ma metody addAll dla typów podstawowych (jako
ints
Jak wyjaśniono w tym artykule , używanie both.toArray(new String[0])będzie szybsze niż both.toArray(new String[both.size()]), nawet jeśli przeczy naszej naiwnej intuicji. Dlatego tak ważne jest, aby zmierzyć rzeczywistą wydajność podczas optymalizacji. Lub po prostu użyj prostszej konstrukcji, gdy nie można udowodnić przewagi bardziej złożonego wariantu.
Holger
42

Rozwiązanie w 100% stare java i bez System.arraycopy (niedostępne na przykład w kliencie GWT):

static String[] concat(String[]... arrays) {
    int length = 0;
    for (String[] array : arrays) {
        length += array.length;
    }
    String[] result = new String[length];
    int pos = 0;
    for (String[] array : arrays) {
        for (String element : array) {
            result[pos] = element;
            pos++;
        }
    }
    return result;
}
francois
źródło
przerobiłem mój plik File [], ale jest taki sam. Dzięki za rozwiązanie
ShadowFlame
5
Prawdopodobnie dość nieefektywny.
JonasCz - Przywróć Monikę
Możesz dodać nullczeki. I być może ustaw niektóre z twoich zmiennych na final.
Tripp Kinetics,
@TrippKinetics nullsprawdza raczej ukrywanie NPE niż ich pokazywanie, a używanie finału dla lokalnych zmiennych nie ma jeszcze żadnych korzyści (jeszcze).
Maarten Bodewes
1
@Maarten Bodewes Myślę, że przekonasz się (jeśli przeprowadzisz test porównawczy, który mam), że for-each działa w tym samym czasie, co indeksowana pętla w późnych wersjach Javy. Optymalizator dba o to.
rghome
33

Ostatnio walczyłem z problemami z nadmierną rotacją pamięci. Jeśli wiadomo, że a i / lub b są zwykle puste, oto kolejna adaptacja kodu silvertab (również wygenerowanego):

private static <T> T[] concatOrReturnSame(T[] a, T[] b) {
    final int alen = a.length;
    final int blen = b.length;
    if (alen == 0) {
        return b;
    }
    if (blen == 0) {
        return a;
    }
    final T[] result = (T[]) java.lang.reflect.Array.
            newInstance(a.getClass().getComponentType(), alen + blen);
    System.arraycopy(a, 0, result, 0, alen);
    System.arraycopy(b, 0, result, alen, blen);
    return result;
}

Edycja: W poprzedniej wersji tego postu stwierdzono, że takie ponowne użycie tablicy powinno być wyraźnie udokumentowane. Jak zauważa Maarten w komentarzach, ogólnie lepiej byłoby po prostu usunąć stwierdzenia if, tym samym unieważniając potrzebę posiadania dokumentacji. Ale z drugiej strony te instrukcje if były przede wszystkim celem tej konkretnej optymalizacji. Zostawię tę odpowiedź tutaj, ale bądź ostrożny!

siatkówki
źródło
5
oznacza to jednak, że zwracana jest ta sama tablica, a zmiana wartości zwróconej tablicy zmienia wartość w tej samej pozycji zwróconej tablicy wejściowej.
Lorenzo Boccaccia
Tak - patrz komentarz na końcu mojego postu dotyczący ponownego użycia tablicy. Nakłady związane z konserwacją narzucone przez to rozwiązanie były tego warte w naszym konkretnym przypadku, ale w większości przypadków prawdopodobnie powinno się zastosować kopiowanie obronne.
siatkówka
Lorenzo / volley, czy możesz wyjaśnić, która część kodu powoduje ponowne użycie tablicy? Myślałem, że System.arraycopykopiuje zawartość tablicy?
Rosdi Kasim
4
Program wywołujący zwykle oczekuje, że wywołanie metody concat () zwróci nowo przydzieloną tablicę. Jeśli albo a lub b ma wartość NULL, concat () zwróci jednak jedną z przekazanych do niej tablic. To ponowne użycie może być nieoczekiwane. (Tak, kopiowanie odbywa się tylko przy kopiowaniu. Ponowne użycie wynika z bezpośredniego zwrotu a lub b.)
volley
Kod powinien być jak najbardziej zrozumiały. Osoby czytające kod nie powinny musieć wyszukiwać JavaDoc wywołanej funkcji, aby dowiedzieć się, że robi jedną rzecz dla jednego określonego warunku, a coś innego dla innej. W skrócie: z reguły nie można naprawić takich problemów projektowych za pomocą komentarza. ifNajprostszym rozwiązaniem byłoby pominięcie dwóch stwierdzeń.
Maarten Bodewes
27

Biblioteka funkcjonalna Java ma klasę otoki tablic, która wyposaża tablice w przydatne metody, takie jak konkatenacja.

import static fj.data.Array.array;

...i wtedy

Array<String> both = array(first).append(array(second));

Aby odzyskać nieopakowaną tablicę, zadzwoń

String[] s = both.array();
Apocalisp
źródło
27
ArrayList<String> both = new ArrayList(Arrays.asList(first));
both.addAll(Arrays.asList(second));

both.toArray(new String[0]);
nick-s
źródło
3
Odpowiedź jest świetna, ale trochę zepsuta. Aby było idealnie, powinieneś przekazać do toArray () tablicę odpowiedniego typu. W powyższym przykładzie kod powinien wyglądać następująco: Both.toArray (nowy ciąg [0]) Patrz: stackoverflow.com/questions/4042434/...
Ronen Rabinovici
Nie wiem, dlaczego ta odpowiedź nie jest oceniana wyżej ... choć wydaje się, że wymaga zmiany sugerowanej przez @RonenRabinovici
drmrbrewer
4
Lub lepiej, bez niepotrzebnego przydziału tablicy zerowej długości:; both.toArray(new String[both.size()]))
Honza
1
@Honza zaleca przeczytanie
Holger
Cześć @Honza, czy można zrobić to samo, aby zwrócić prymitywną tablicę liczb całkowitych w 3 wierszach?
jumping_monkey
18

Kolejny sposób z Java8 za pomocą Stream

  public String[] concatString(String[] a, String[] b){ 
    Stream<String> streamA = Arrays.stream(a);
    Stream<String> streamB = Arrays.stream(b);
    return Stream.concat(streamA, streamB).toArray(String[]::new); 
  }
Wazony
źródło
17

Oto adaptacja rozwiązania silvertab, z doposażonymi lekami generycznymi:

static <T> T[] concat(T[] a, T[] b) {
    final int alen = a.length;
    final int blen = b.length;
    final T[] result = (T[]) java.lang.reflect.Array.
            newInstance(a.getClass().getComponentType(), alen + blen);
    System.arraycopy(a, 0, result, 0, alen);
    System.arraycopy(b, 0, result, alen, blen);
    return result;
}

UWAGA: Zobacz odpowiedź Joachima na rozwiązanie Java 6. Nie tylko eliminuje to ostrzeżenie; jest również krótszy, wydajniejszy i łatwiejszy do odczytania!

wolej
źródło
Możesz wyłączyć ostrzeżenie dla tej metody, ale poza tym niewiele możesz zrobić. Tablice i generyczne tak naprawdę się nie mieszają.
Dan Dyer,
3
Niesprawdzone ostrzeżenie można wyeliminować, jeśli użyjesz Arrays.copyOf (). Zobacz moją odpowiedź na wdrożenie.
Joachim Sauer
@SuppressWarnings („niezaznaczone”)
Mark Renouf
13

Jeśli korzystasz z tej metody, nie musisz importować żadnych klas stron trzecich.

Jeśli chcesz konkatenować String

Przykładowy kod dla concate two String Array

public static String[] combineString(String[] first, String[] second){
        int length = first.length + second.length;
        String[] result = new String[length];
        System.arraycopy(first, 0, result, 0, first.length);
        System.arraycopy(second, 0, result, first.length, second.length);
        return result;
    }

Jeśli chcesz konkatenować Int

Przykładowy kod dla concate two Integer Array

public static int[] combineInt(int[] a, int[] b){
        int length = a.length + b.length;
        int[] result = new int[length];
        System.arraycopy(a, 0, result, 0, a.length);
        System.arraycopy(b, 0, result, a.length, b.length);
        return result;
    }

Oto główna metoda

    public static void main(String[] args) {

            String [] first = {"a", "b", "c"};
            String [] second = {"d", "e"};

            String [] joined = combineString(first, second);
            System.out.println("concatenated String array : " + Arrays.toString(joined));

            int[] array1 = {101,102,103,104};
            int[] array2 = {105,106,107,108};
            int[] concatenateInt = combineInt(array1, array2);

            System.out.println("concatenated Int array : " + Arrays.toString(concatenateInt));

        }
    }  

Z tego też możemy korzystać.

Raj S. Rusia
źródło
11

Proszę wybaczyć, że dodałem kolejną wersję do tej i tak już długiej listy. Przejrzałem każdą odpowiedź i zdecydowałem, że naprawdę chcę wersję z jednym parametrem w podpisie. Dodałem także pewne sprawdzanie argumentów, aby skorzystać z wczesnego niepowodzenia z sensownymi informacjami w przypadku nieoczekiwanego wejścia.

@SuppressWarnings("unchecked")
public static <T> T[] concat(T[]... inputArrays) {
  if(inputArrays.length < 2) {
    throw new IllegalArgumentException("inputArrays must contain at least 2 arrays");
  }

  for(int i = 0; i < inputArrays.length; i++) {
    if(inputArrays[i] == null) {
      throw new IllegalArgumentException("inputArrays[" + i + "] is null");
    }
  }

  int totalLength = 0;

  for(T[] array : inputArrays) {
    totalLength += array.length;
  }

  T[] result = (T[]) Array.newInstance(inputArrays[0].getClass().getComponentType(), totalLength);

  int offset = 0;

  for(T[] array : inputArrays) {
    System.arraycopy(array, 0, result, offset, array.length);

    offset += array.length;
  }

  return result;
}
Zalumon
źródło
Podsumowałbym długość w tej samej pętli, w której przeprowadzasz kontrolę zerową - ale to jest naprawdę dobre podsumowanie innych odpowiedzi tutaj. Wierzę, że obsługuje nawet wewnętrzne typy, takie jak „int”, bez zamiany ich na obiekty typu Integer, co jest naprawdę JEDYNYM powodem, aby traktować je jako tablice, a nie tylko zmieniać wszystko na ArrayLists. Również twoja metoda może przyjmować 2 tablice i parametr (...), aby osoba dzwoniąca wiedziała, że ​​musi przekazać co najmniej dwie tablice, zanim ją uruchomi i zobaczy błąd, ale to komplikuje kod zapętlający ....
Bill K
11

Możesz spróbować przekonwertować go na Arraylist i użyć metody addAll, a następnie przekonwertować z powrotem na tablicę.

List list = new ArrayList(Arrays.asList(first));
  list.addAll(Arrays.asList(second));
  String[] both = list.toArray();
Paweł
źródło
Dobre rozwiązanie - byłoby lepiej, gdyby kod został przeredagowany, aby całkowicie uniknąć tablic na korzyść ArrayLists, ale to jest poza kontrolą odpowiedzi i odpowiedzi na pytanie.
Bill K
Liczę, że do działania potrzeba 4 dodatkowych obiektów tymczasowych.
rghome
@rghome, przynajmniej nie wymaga dodatkowej biblioteki do wykonania tak prostego zadania
Farid
9

Za pomocą strumieni Java 8+ możesz napisać następującą funkcję:

private static String[] concatArrays(final String[]... arrays) {
    return Arrays.stream(arrays)
         .flatMap(Arrays::stream)
         .toArray(String[]::new);
}
keisar
źródło
7

Tutaj możliwa implementacja w działającym kodzie rozwiązania pseudokodu napisanego przez silvertab.

Dzięki silvertab!

public class Array {

   public static <T> T[] concat(T[] a, T[] b, ArrayBuilderI<T> builder) {
      T[] c = builder.build(a.length + b.length);
      System.arraycopy(a, 0, c, 0, a.length);
      System.arraycopy(b, 0, c, a.length, b.length);
      return c;
   }
}

Następny jest interfejs konstruktora.

Uwaga: Konstruktor jest konieczny, ponieważ w Javie nie jest to możliwe

new T[size]

z powodu ogólnego usunięcia typu:

public interface ArrayBuilderI<T> {

   public T[] build(int size);
}

Oto konkretny konstruktor implementujący interfejs, budujący Integertablicę:

public class IntegerArrayBuilder implements ArrayBuilderI<Integer> {

   @Override
   public Integer[] build(int size) {
      return new Integer[size];
   }
}

I na koniec aplikacja / test:

@Test
public class ArrayTest {

   public void array_concatenation() {
      Integer a[] = new Integer[]{0,1};
      Integer b[] = new Integer[]{2,3};
      Integer c[] = Array.concat(a, b, new IntegerArrayBuilder());
      assertEquals(4, c.length);
      assertEquals(0, (int)c[0]);
      assertEquals(1, (int)c[1]);
      assertEquals(2, (int)c[2]);
      assertEquals(3, (int)c[3]);
   }
}
hpgisler
źródło
7

To powinno być jedno-liniowe.

public String [] concatenate (final String array1[], final String array2[])
{
    return Stream.concat(Stream.of(array1), Stream.of(array2)).toArray(String[]::new);
}
avigaild
źródło
6

Łał! wiele skomplikowanych odpowiedzi tutaj, w tym kilka prostych, które zależą od zewnętrznych zależności. co powiesz na zrobienie tego w ten sposób:

String [] arg1 = new String{"a","b","c"};
String [] arg2 = new String{"x","y","z"};

ArrayList<String> temp = new ArrayList<String>();
temp.addAll(Arrays.asList(arg1));
temp.addAll(Arrays.asList(arg2));
String [] concatedArgs = temp.toArray(new String[arg1.length+arg2.length]);
doles
źródło
1
..Ale nieefektywny i powolny.
JonasCz - Przywróć Monikę
6

To działa, ale musisz wstawić własne sprawdzanie błędów.

public class StringConcatenate {

    public static void main(String[] args){

        // Create two arrays to concatenate and one array to hold both
        String[] arr1 = new String[]{"s","t","r","i","n","g"};
        String[] arr2 = new String[]{"s","t","r","i","n","g"};
        String[] arrBoth = new String[arr1.length+arr2.length];

        // Copy elements from first array into first part of new array
        for(int i = 0; i < arr1.length; i++){
            arrBoth[i] = arr1[i];
        }

        // Copy elements from second array into last part of new array
        for(int j = arr1.length;j < arrBoth.length;j++){
            arrBoth[j] = arr2[j-arr1.length];
        }

        // Print result
        for(int k = 0; k < arrBoth.length; k++){
            System.out.print(arrBoth[k]);
        }

        // Additional line to make your terminal look better at completion!
        System.out.println();
    }
}

Prawdopodobnie nie jest to najbardziej wydajny, ale nie opiera się na niczym innym niż własny interfejs API Java.

klej
źródło
2
+1. Lepiej byłoby zamienić drugą forpętlę na:for(int j = 0; j < arr2.length; j++){arrBoth[arr1.length+j] = arr2[j];}
bancer 28.10.10
Użyj, String[] arrBoth = java.util.Arrays.copyOf(arr1, arr1.length + arr2.length)aby pominąć pierwszą forpętlę. Oszczędza czas proporcjonalny do wielkości arr1.
John Meyer
5

To jest przekonwertowana funkcja dla tablicy String:

public String[] mergeArrays(String[] mainArray, String[] addArray) {
    String[] finalArray = new String[mainArray.length + addArray.length];
    System.arraycopy(mainArray, 0, finalArray, 0, mainArray.length);
    System.arraycopy(addArray, 0, finalArray, mainArray.length, addArray.length);

    return finalArray;
}
Oritm
źródło
5

A może po prostu

public static class Array {

    public static <T> T[] concat(T[]... arrays) {
        ArrayList<T> al = new ArrayList<T>();
        for (T[] one : arrays)
            Collections.addAll(al, one);
        return (T[]) al.toArray(arrays[0].clone());
    }
}

I po prostu zrób Array.concat(arr1, arr2). Dopóki są arr1i arr2są tego samego typu, daje to kolejną tablicę tego samego typu zawierającą obie tablice.

Efraim
źródło
Ze względu na wydajność wstępnie obliczyłem ostateczny rozmiar ArrayList, ponieważ ArrayList z definicji przydziela nową tablicę i kopiuje jej elementy za każdym razem, gdy bieżąca tablica jest pełna. W przeciwnym razie poszedłbym prosto do LinkedList, który nie cierpi takiego problemu
usr-local-ΕΨΗΕΛΩ
5

Ogólna wersja statyczna, która wykorzystuje wysoce wydajny System.arraycopy bez wymagania adnotacji @SuppressWarnings:

public static <T> T[] arrayConcat(T[] a, T[] b) {
    T[] both = Arrays.copyOf(a, a.length + b.length);
    System.arraycopy(b, 0, both, a.length, b.length);
    return both;
}
beaudet
źródło
4
public String[] concat(String[]... arrays)
{
    int length = 0;
    for (String[] array : arrays) {
        length += array.length;
    }
    String[] result = new String[length];
    int destPos = 0;
    for (String[] array : arrays) {
        System.arraycopy(array, 0, result, destPos, array.length);
        destPos += array.length;
    }
    return result;
}
Sujay
źródło
4

Oto moja nieznacznie ulepszona wersja concatAll Joachima Sauera. Może pracować na Javie 5 lub 6, używając System.arraycopy Java 6, jeśli jest dostępny w czasie wykonywania. Ta metoda (IMHO) jest idealna dla Androida, ponieważ działa na Androidzie <9 (który nie ma System.arraycopy), ale w miarę możliwości zastosuje szybszą metodę.

  public static <T> T[] concatAll(T[] first, T[]... rest) {
    int totalLength = first.length;
    for (T[] array : rest) {
      totalLength += array.length;
    }
    T[] result;
    try {
      Method arraysCopyOf = Arrays.class.getMethod("copyOf", Object[].class, int.class);
      result = (T[]) arraysCopyOf.invoke(null, first, totalLength);
    } catch (Exception e){
      //Java 6 / Android >= 9 way didn't work, so use the "traditional" approach
      result = (T[]) java.lang.reflect.Array.newInstance(first.getClass().getComponentType(), totalLength);
      System.arraycopy(first, 0, result, 0, first.length);
    }
    int offset = first.length;
    for (T[] array : rest) {
      System.arraycopy(array, 0, result, offset, array.length);
      offset += array.length;
    }
    return result;
  }
świeczniki
źródło
1
Dobry ogólny pomysł, ale dla każdego, kto wdraża: wolałbym wersje copyOf i non-copyOf niż te, które robią to zarówno w wyniku refleksji.
rektide
4

Inny sposób myślenia o pytaniu. Aby połączyć dwie lub więcej tablic, wystarczy zrobić listę wszystkich elementów każdej z tablic, a następnie zbudować nową tablicę. To brzmi jak utwórz List<T>a następnie wywołuje toArray. Używasz innych odpowiedzi ArrayListi to jest w porządku. Ale co powiesz na wdrożenie naszego własnego? To nie jest trudne:

private static <T> T[] addAll(final T[] f, final T...o){
    return new AbstractList<T>(){

        @Override
        public T get(int i) {
            return i>=f.length ? o[i - f.length] : f[i];
        }

        @Override
        public int size() {
            return f.length + o.length;
        }

    }.toArray(f);
}

Uważam, że powyższe jest równoważne rozwiązaniom, które wykorzystują System.arraycopy. Myślę jednak, że ten ma swoje piękno.

Silnik Ziemi
źródło
4

Co powiesz na :

public String[] combineArray (String[] ... strings) {
    List<String> tmpList = new ArrayList<String>();
    for (int i = 0; i < strings.length; i++)
        tmpList.addAll(Arrays.asList(strings[i]));
    return tmpList.toArray(new String[tmpList.size()]);
}
clément francomme
źródło
4

Prosta odmiana umożliwiająca połączenie więcej niż jednej tablicy:

public static String[] join(String[]...arrays) {

    final List<String> output = new ArrayList<String>();

    for(String[] array : arrays) {
        output.addAll(Arrays.asList(array));
    }

    return output.toArray(new String[output.size()]);
}
Damo
źródło
3

Używanie tylko własnego interfejsu API języka JavaScript:


String[] join(String[]... arrays) {
  // calculate size of target array
  int size = 0;
  for (String[] array : arrays) {
    size += array.length;
  }

  // create list of appropriate size
  java.util.List list = new java.util.ArrayList(size);

  // add arrays
  for (String[] array : arrays) {
    list.addAll(java.util.Arrays.asList(array));
  }

  // create and return final array
  return list.toArray(new String[size]);
}

Teraz ten kod nie jest najbardziej wydajny, ale opiera się tylko na standardowych klasach Java i jest łatwy do zrozumienia. Działa dla dowolnej liczby String [] (nawet zerowych tablic).

kiwi
źródło
15
Musiałem zanegować ten jeden za wszystkie niepotrzebne tworzenie obiektów List.
Outlaw Programmer