Muszę połączyć dwie String
tablice w Javie.
void f(String[] first, String[] second) {
String[] both = ???
}
Jak najłatwiej to zrobić?
array1 + array2
konkatenacji.
Muszę połączyć dwie String
tablice w Javie.
void f(String[] first, String[] second) {
String[] both = ???
}
Jak najłatwiej to zrobić?
array1 + array2
konkatenacji.
Odpowiedzi:
Znalazłem jedno-liniowe rozwiązanie ze starej dobrej biblioteki Apache Commons Lang.
ArrayUtils.addAll(T[], T...)
Kod:
String[] both = ArrayUtils.addAll(first, second);
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ą T
zamiast 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 }));
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.
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 List
obiektó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;
}
T
z nich byte
(i stracisz <T>
).
ByteBuffer buffer = ByteBuffer.allocate(array1.length + array2.length); buffer.put(array1); buffer.put(array2); return buffer.array();
concat(ai, ad)
gdzie ai
jest Integer[]
i ad
jest Double[]
. (W tym przypadku parametr Typ <T>
zostanie rozwiązany <? extends Number>
przez kompilator). Tablicy utworzonej przez Arrays.copyOf
będzie miał typu części pierwszej tablicy, to znaczy Integer
w tym przykładzie. Gdy funkcja ma zamiar skopiować drugą tablicę, ArrayStoreException
zostanie wyrzucony znak an . Rozwiązaniem jest mieć dodatkowy Class<T> type
parametr.
Korzystanie Stream
z 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));
.boxed()
takie, aby były typu, Stream
a nie np. IntStream
Które nie mogą być przekazane jako parametr do Stream.concat
.
a
i b
są int[]
, użyjint[] both = IntStream.concat(Arrays.stream(a), Arrays.stream(b)).toArray();
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.
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)
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ć for
pętli dla dużych tablic, ponieważ nie są one wydajne. Wbudowane metody wykorzystują funkcje szybkiego kopiowania bloków.
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()]);
}
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.
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;
}
null
czeki. I być może ustaw niektóre z twoich zmiennych na final
.
null
sprawdza raczej ukrywanie NPE niż ich pokazywanie, a używanie finału dla lokalnych zmiennych nie ma jeszcze żadnych korzyści (jeszcze).
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!
System.arraycopy
kopiuje zawartość tablicy?
if
Najprostszym rozwiązaniem byłoby pominięcie dwóch stwierdzeń.
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();
ArrayList<String> both = new ArrayList(Arrays.asList(first));
both.addAll(Arrays.asList(second));
both.toArray(new String[0]);
both.toArray(new String[both.size()])
)
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!
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ć.
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;
}
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();
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 Integer
tablicę:
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]);
}
}
Ł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]);
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.
for
pętlę na:for(int j = 0; j < arr2.length; j++){arrBoth[arr1.length+j] = arr2[j];}
String[] arrBoth = java.util.Arrays.copyOf(arr1, arr1.length + arr2.length)
aby pominąć pierwszą for
pętlę. Oszczędza czas proporcjonalny do wielkości arr1
.
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;
}
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ą arr1
i arr2
są tego samego typu, daje to kolejną tablicę tego samego typu zawierającą obie tablice.
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;
}
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;
}
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;
}
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 ArrayList
i 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.
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()]);
}
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()]);
}
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).