Devo concatenare due String
array in Java.
void f(String[] first, String[] second) {
String[] both = ???
}
Qual è il modo più semplice per farlo?
array1 + array2
concatenazione.
Devo concatenare due String
array in Java.
void f(String[] first, String[] second) {
String[] both = ???
}
Qual è il modo più semplice per farlo?
array1 + array2
concatenazione.
Risposte:
Ho trovato una soluzione a una riga dalla buona vecchia biblioteca di Apache Commons Lang.
ArrayUtils.addAll(T[], T...)
Codice:
String[] both = ArrayUtils.addAll(first, second);
Ecco un metodo semplice che concatenerà due matrici e restituirà il risultato:
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;
}
Si noti che non funzionerà con tipi di dati primitivi, ma solo con tipi di oggetto.
La seguente versione leggermente più complicata funziona con matrici di oggetti e primitive. Lo fa usando T
invece T[]
come tipo di argomento.
Inoltre, consente di concatenare matrici di due tipi diversi selezionando il tipo più generale come tipo di componente del risultato.
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;
}
Ecco un esempio:
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);
. Sorprendentemente non l'ho mai visto prima. @beaudet Penso che l'annotazione vada bene qui, considerando il motivo per cui viene soppressa.
È possibile scrivere una versione completamente generica che può anche essere estesa per concatenare un numero qualsiasi di matrici. Queste versioni richiedono Java 6, in quanto utilizzanoArrays.copyOf()
Entrambe le versioni evitano di creare List
oggetti intermedi e lo utilizzano System.arraycopy()
per garantire che la copia di array di grandi dimensioni sia il più rapida possibile.
Per due array si presenta così:
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;
}
E per un numero arbitrario di array (> = 1) è simile al seguente:
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
con byte
(e perdere il <T>
).
ByteBuffer buffer = ByteBuffer.allocate(array1.length + array2.length); buffer.put(array1); buffer.put(array2); return buffer.array();
concat(ai, ad)
dove si ai
trova Integer[]
ed ad
è Double[]
. (In questo caso, il parametro type <T>
viene risolto <? extends Number>
dal compilatore.) L'array creato da Arrays.copyOf
avrà il tipo di componente del primo array, ovvero Integer
in questo esempio. Quando la funzione sta per copiare il secondo array, ArrayStoreException
verrà lanciato un. La soluzione è avere un Class<T> type
parametro aggiuntivo .
Utilizzando Stream
in Java 8:
String[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b))
.toArray(String[]::new);
O in questo modo, usando flatMap
:
String[] both = Stream.of(a, b).flatMap(Stream::of)
.toArray(String[]::new);
Per fare questo per un tipo generico devi usare la riflessione:
@SuppressWarnings("unchecked")
T[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b)).toArray(
size -> (T[]) Array.newInstance(a.getClass().getComponentType(), size));
.boxed()
tali da essere di tipo Stream
piuttosto che ad esempio IntStream
che non possono essere passati come parametro a Stream.concat
.
a
e lo b
sono int[]
, usaint[] both = IntStream.concat(Arrays.stream(a), Arrays.stream(b)).toArray();
System.arrayCopy
. Ma neanche particolarmente lento. Probabilmente fare questo un molto molte volte con enormi array in realtà contesti sensibili alle prestazioni, per la differenza di tempo di esecuzione alla materia.
O con l'amata Guava :
String[] both = ObjectArrays.concat(first, second, String.class);
Inoltre, ci sono versioni per array primitivi:
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)
È possibile aggiungere le due matrici in due righe di codice.
String[] both = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, both, first.length, second.length);
Questa è una soluzione rapida ed efficiente e funzionerà per tipi primitivi e i due metodi coinvolti sono sovraccarichi.
È necessario evitare soluzioni che coinvolgono ArrayList, stream, ecc. In quanto dovranno allocare memoria temporanea senza uno scopo utile.
È necessario evitare for
loop per array di grandi dimensioni poiché questi non sono efficienti. I metodi integrati utilizzano funzioni di copia a blocchi estremamente veloci.
Utilizzando l'API Java:
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])
sarà più veloce di both.toArray(new String[both.size()])
, anche se contraddice la nostra ingenua intuizione. Ecco perché è così importante misurare le prestazioni effettive durante l'ottimizzazione. O semplicemente usa il costrutto più semplice, quando il vantaggio della variante più complicata non può essere dimostrato.
Una soluzione java al 100% e senza System.arraycopy
(non disponibile nel client GWT per esempio):
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
assegni. E forse imposta alcune delle tue variabili su final
.
null
controlli di @TrippKinetics nascondono gli NPE anziché mostrarli e l'utilizzo di final per i var locali non ha alcun vantaggio (ancora).
Di recente ho combattuto i problemi con l'eccessiva rotazione della memoria. Se a e / o b sono noti per essere comunemente vuoti, ecco un altro adattamento del codice di silvertab (anch'esso generato):
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;
}
Modifica: una versione precedente di questo post affermava che il riutilizzo dell'array come questo deve essere chiaramente documentato. Come sottolinea Maarten nei commenti, in generale sarebbe meglio rimuovere le dichiarazioni if, annullando così la necessità di disporre di documentazione. Ma ancora una volta, quelle dichiarazioni if erano il punto centrale di questa particolare ottimizzazione in primo luogo. Lascio qui questa risposta, ma fai attenzione!
System.arraycopy
copiare il contenuto dell'array?
if
fuori le due affermazioni sarebbe la soluzione più semplice.
La libreria Java funzionale ha una classe wrapper di array che fornisce array con metodi utili come la concatenazione.
import static fj.data.Array.array;
...e poi
Array<String> both = array(first).append(array(second));
Per riavere l'array da scartare, chiamare
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()])
Ecco un adattamento della soluzione di silvertab, con generici retrofittati:
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;
}
NOTA: vedere la risposta di Joachim per una soluzione Java 6. Non solo elimina l'avvertimento; è anche più breve, più efficiente e più facile da leggere!
Se si utilizza in questo modo, non è necessario importare alcuna classe di terze parti.
Se vuoi concatenare String
Codice di esempio per concare due array di stringhe
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;
}
Se vuoi concatenare Int
Codice di esempio per concare due numeri interi
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;
}
Ecco il metodo principale
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));
}
}
Possiamo usare anche in questo modo.
Per favore, perdonami per aver aggiunto un'altra versione a questo elenco già lungo. Ho guardato ogni risposta e ho deciso che volevo davvero una versione con un solo parametro nella firma. Ho anche aggiunto alcuni argomenti di verifica per trarre vantaggio da guasti precoci con informazioni sensibili in caso di input imprevisti.
@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;
}
Potresti provare a convertirlo in un Arraylist e utilizzare il metodo addAll, quindi riconvertire in un array.
List list = new ArrayList(Arrays.asList(first));
list.addAll(Arrays.asList(second));
String[] both = list.toArray();
Qui una possibile implementazione nel codice di lavoro della soluzione di pseudo codice scritta da silvertab.
Grazie 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;
}
}
Di seguito è riportata l'interfaccia del builder.
Nota: un builder è necessario perché in java non è possibile farlo
new T[size]
a causa della cancellazione di tipo generico:
public interface ArrayBuilderI<T> {
public T[] build(int size);
}
Qui un builder concreto che implementa l'interfaccia, costruendo un Integer
array:
public class IntegerArrayBuilder implements ArrayBuilderI<Integer> {
@Override
public Integer[] build(int size) {
return new Integer[size];
}
}
E infine l'applicazione / 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]);
}
}
Wow! molte risposte complesse qui, incluse alcune semplici che dipendono da dipendenze esterne. che ne dici di farlo in questo modo:
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]);
Funziona, ma devi inserire il tuo controllo degli errori.
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();
}
}
Probabilmente non è il più efficiente, ma non si basa su qualcosa di diverso dall'API di Java.
for
loop con quello: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)
per saltare il primo for
loop. Risparmia tempo proporzionale alla dimensione di arr1
.
Questa è una funzione convertita per un array 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;
}
Che ne dici semplicemente
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());
}
}
E basta Array.concat(arr1, arr2)
. Finché arr1
e arr2
sono dello stesso tipo, questo ti darà un altro array dello stesso tipo contenente entrambi gli array.
Una versione statica generica che utilizza System.arraycopy ad alte prestazioni senza richiedere un'annotazione @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;
}
Ecco la mia versione leggermente migliorata del concatAll di Joachim Sauer. Può funzionare su Java 5 o 6, utilizzando System.arraycopy di Java 6 se è disponibile in fase di esecuzione. Questo metodo (IMHO) è perfetto per Android, poiché funziona su Android <9 (che non ha System.arraycopy) ma utilizzerà il metodo più veloce se possibile.
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;
}
Un altro modo di pensare alla domanda. Per concatenare due o più array, è necessario elencare tutti gli elementi di ciascun array e quindi creare un nuovo array. Questo suona come creare un List<T>
e quindi lo chiama toArray
. Alcune altre risposte usano ArrayList
, e va bene. Ma che ne dici di implementare il nostro? Non è difficile:
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);
}
Credo che quanto sopra sia equivalente a soluzioni che utilizza System.arraycopy
. Comunque penso che questo abbia una sua bellezza.
Che ne dite di :
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()]);
}
Utilizzando solo l'API propria di Javas:
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]);
}
Ora, questo codice non è il più efficiente, ma si basa solo su classi java standard ed è facile da capire. Funziona con qualsiasi numero di String [] (anche zero array).