J'ai besoin de concaténer deux String
tableaux en Java.
void f(String[] first, String[] second) {
String[] both = ???
}
Quelle est la manière la plus simple de faire ça?
array1 + array2
concaténation.
J'ai besoin de concaténer deux String
tableaux en Java.
void f(String[] first, String[] second) {
String[] both = ???
}
Quelle est la manière la plus simple de faire ça?
array1 + array2
concaténation.
Réponses:
J'ai trouvé une solution en ligne de la bonne vieille bibliothèque Apache Commons Lang.
ArrayUtils.addAll(T[], T...)
Code:
String[] both = ArrayUtils.addAll(first, second);
Voici une méthode simple qui concaténera deux tableaux et retournera le résultat:
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;
}
Notez qu'il ne fonctionnera pas avec les types de données primitifs, uniquement avec les types d'objets.
La version suivante, légèrement plus compliquée, fonctionne avec les tableaux d'objets et les tableaux primitifs. Il le fait en utilisant T
au lieu de T[]
comme type d'argument.
Il permet également de concaténer des tableaux de deux types différents en choisissant le type le plus général comme type de composant du résultat.
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;
}
Voici un exemple:
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);
. Je n'ai étonnamment jamais vu ça auparavant. @beaudet Je pense que l'annotation est très bien ici, vu pourquoi elle est supprimée.
Il est possible d'écrire une version entièrement générique qui peut même être étendue pour concaténer n'importe quel nombre de tableaux. Ces versions nécessitent Java 6, car elles utilisentArrays.copyOf()
Les deux versions évitent de créer des List
objets intermédiaires et les utilisent System.arraycopy()
pour garantir que la copie de grands tableaux est aussi rapide que possible.
Pour deux tableaux, cela ressemble à ceci:
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;
}
Et pour un nombre arbitraire de tableaux (> = 1), cela ressemble à ceci:
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
par byte
(et perdez le <T>
).
ByteBuffer buffer = ByteBuffer.allocate(array1.length + array2.length); buffer.put(array1); buffer.put(array2); return buffer.array();
concat(ai, ad)
, où ai
est Integer[]
et ad
est Double[]
. (Dans ce cas, le paramètre type <T>
est résolu <? extends Number>
par le compilateur.) Le tableau créé par Arrays.copyOf
aura le type de composant du premier tableau, c'est- Integer
à- dire dans cet exemple. Lorsque la fonction est sur le point de copier le deuxième tableau, un ArrayStoreException
sera lancé. La solution est d'avoir un Class<T> type
paramètre supplémentaire .
Utilisation Stream
en Java 8:
String[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b))
.toArray(String[]::new);
Ou comme ça, en utilisant flatMap
:
String[] both = Stream.of(a, b).flatMap(Stream::of)
.toArray(String[]::new);
Pour ce faire, pour un type générique, vous devez utiliser la réflexion:
@SuppressWarnings("unchecked")
T[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b)).toArray(
size -> (T[]) Array.newInstance(a.getClass().getComponentType(), size));
.boxed()
afin qu'ils soient de type Stream
plutôt que par exemple IntStream
qui ne peuvent pas être passés en paramètre à Stream.concat
.
a
et b
êtes int[]
, utilisezint[] both = IntStream.concat(Arrays.stream(a), Arrays.stream(b)).toArray();
System.arrayCopy
. Mais pas particulièrement lent non plus. Vous devrez probablement le faire très souvent avec des tableaux énormes dans des contextes très sensibles aux performances pour que la différence de temps d'exécution soit importante.
Ou avec la bien-aimée Goyave :
String[] both = ObjectArrays.concat(first, second, String.class);
Il existe également des versions pour les tableaux primitifs:
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)
Vous pouvez ajouter les deux tableaux dans deux lignes de code.
String[] both = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, both, first.length, second.length);
Il s'agit d'une solution rapide et efficace qui fonctionnera pour les types primitifs ainsi que les deux méthodes impliquées sont surchargées.
Vous devez éviter les solutions impliquant des listes de tableaux, des flux, etc. car celles-ci devront allouer de la mémoire temporaire sans but utile.
Vous devez éviter les for
boucles pour les grands tableaux car ceux-ci ne sont pas efficaces. Les méthodes intégrées utilisent des fonctions de copie de blocs extrêmement rapides.
Utilisation de 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])
sera plus rapide que both.toArray(new String[both.size()])
, même si elle contredit notre intuition naïve. C'est pourquoi il est si important de mesurer les performances réelles lors de l'optimisation. Ou utilisez simplement la construction plus simple, lorsque l'avantage de la variante plus compliquée ne peut pas être prouvé.
Une solution 100% java ancienne et sans System.arraycopy
(non disponible dans le client GWT par exemple):
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
chèques. Et peut-être définir certaines de vos variables final
.
null
vérifications @TrippKinetics masqueraient les NPE plutôt que de les afficher et l'utilisation de final pour les var locales n'a aucun avantage (pour l'instant).
J'ai récemment combattu des problèmes de rotation excessive de la mémoire. Si a et / ou b sont connus pour être généralement vides, voici une autre adaptation du code de silvertab (également généré):
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;
}
Edit: Une version précédente de ce message a déclaré que la réutilisation de la baie comme celle-ci doit être clairement documentée. Comme le souligne Maarten dans les commentaires, il serait généralement préférable de simplement supprimer les instructions if, ce qui rendrait inutile la documentation. Mais là encore, ces déclarations if étaient le point entier de cette optimisation particulière en premier lieu. Je vais laisser cette réponse ici, mais méfiez-vous!
System.arraycopy
le contenu du tableau était copié?
if
simple fait de laisser de côté les deux déclarations serait la solution la plus simple.
La bibliothèque Java fonctionnelle possède une classe d'encapsuleur de tableaux qui équipe les tableaux de méthodes pratiques comme la concaténation.
import static fj.data.Array.array;
...et alors
Array<String> both = array(first).append(array(second));
Pour récupérer le tableau déballé, appelez
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()])
Voici une adaptation de la solution de silvertab, avec des génériques modernisés:
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;
}
REMARQUE: voir la réponse de Joachim pour une solution Java 6. Non seulement cela élimine l'avertissement; il est également plus court, plus efficace et plus facile à lire!
Si vous utilisez cette méthode, vous n'avez donc pas besoin d'importer de classe tierce.
Si vous voulez concaténer String
Exemple de code pour concéder deux tableaux de chaînes
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;
}
Si vous voulez concaténer Int
Exemple de code pour concéder deux tableaux entiers
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;
}
Voici la méthode 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));
}
}
Nous pouvons également utiliser cette méthode.
Veuillez me pardonner d'avoir ajouté une autre version à cette liste déjà longue. J'ai regardé chaque réponse et j'ai décidé que je voulais vraiment une version avec un seul paramètre dans la signature. J'ai également ajouté quelques vérifications d'arguments pour bénéficier d'un échec précoce avec des informations sensibles en cas d'entrée inattendue.
@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;
}
Vous pouvez essayer de le convertir en Arraylist et utiliser la méthode addAll puis reconvertir en tableau.
List list = new ArrayList(Arrays.asList(first));
list.addAll(Arrays.asList(second));
String[] both = list.toArray();
Voici une implémentation possible en code de travail de la solution de pseudo code écrite par silvertab.
Merci 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;
}
}
Voici ensuite l'interface du générateur.
Remarque: Un constructeur est nécessaire car en java il n'est pas possible de faire
new T[size]
en raison de l'effacement de type générique:
public interface ArrayBuilderI<T> {
public T[] build(int size);
}
Voici un constructeur concret implémentant l'interface, construisant un Integer
tableau:
public class IntegerArrayBuilder implements ArrayBuilderI<Integer> {
@Override
public Integer[] build(int size) {
return new Integer[size];
}
}
Et enfin l'application / 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]);
}
}
Hou la la! beaucoup de réponses complexes ici, y compris des réponses simples qui dépendent de dépendances externes. que diriez-vous de faire comme ça:
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]);
Cela fonctionne, mais vous devez insérer votre propre vérification d'erreur.
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();
}
}
Ce n'est probablement pas le plus efficace, mais il ne repose sur rien d'autre que la propre API de Java.
for
boucle par celle-ci: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)
pour sauter la première for
boucle. Gain de temps proportionnel à la taille de arr1
.
Il s'agit d'une fonction convertie pour un tableau 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;
}
Que diriez-vous simplement
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());
}
}
Et fais-le Array.concat(arr1, arr2)
. Tant que arr1
et arr2
sont du même type, cela vous donnera un autre tableau du même type contenant les deux tableaux.
Une version statique générique qui utilise le System.arraycopy très performant sans nécessiter une annotation @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;
}
Voici ma version légèrement améliorée de concatAll de Joachim Sauer. Il peut fonctionner sur Java 5 ou 6, en utilisant System.arraycopy de Java 6 s'il est disponible au moment de l'exécution. Cette méthode (à mon humble avis) est parfaite pour Android, car elle fonctionne sur Android <9 (qui n'a pas System.arraycopy) mais utilisera la méthode la plus rapide si possible.
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;
}
Une autre façon de penser la question. Pour concaténer deux tableaux ou plus, il suffit de répertorier tous les éléments de chaque tableau, puis de créer un nouveau tableau. Cela ressemble à créer un List<T>
, puis l'appelle toArray
. Certaines autres réponses sont utiles ArrayList
, et c'est très bien. Mais que diriez-vous de mettre en œuvre le nôtre? Ce n'est pas 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);
}
Je crois que ce qui précède est équivalent aux solutions qui utilisent System.arraycopy
. Cependant, je pense que celui-ci a sa propre beauté.
Que diriez-vous :
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()]);
}
Une variante simple permettant la jonction de plus d'un tableau:
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()]);
}
Utiliser uniquement la propre API de 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]);
}
Maintenant, ce code n'est pas le plus efficace, mais il ne repose que sur les classes java standard et est facile à comprendre. Cela fonctionne pour n'importe quel nombre de String [] (même zéro tableau).