Necesito concatenar dos String
matrices en Java.
void f(String[] first, String[] second) {
String[] both = ???
}
¿Cuál es la forma más fácil de hacer esto?
array1 + array2
concatenación simple .
Necesito concatenar dos String
matrices en Java.
void f(String[] first, String[] second) {
String[] both = ???
}
¿Cuál es la forma más fácil de hacer esto?
array1 + array2
concatenación simple .
Respuestas:
Encontré una solución de una línea de la vieja y buena biblioteca Apache Commons Lang.
ArrayUtils.addAll(T[], T...)
Código:
String[] both = ArrayUtils.addAll(first, second);
Aquí hay un método simple que concatenará dos matrices y devolverá el resultado:
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;
}
Tenga en cuenta que no funcionará con tipos de datos primitivos, solo con tipos de objetos.
La siguiente versión un poco más complicada funciona con objetos y matrices primitivas. Lo hace utilizando en T
lugar de T[]
como el tipo de argumento.
También hace posible concatenar matrices de dos tipos diferentes seleccionando el tipo más general como el tipo de componente del resultado.
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;
}
Aquí hay un ejemplo:
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, nunca he visto eso antes. @beaudet Creo que la anotación está bien aquí, considerando por qué está siendo suprimida.
Es posible escribir una versión totalmente genérica que incluso se puede extender para concatenar cualquier número de matrices. Estas versiones requieren Java 6, ya que usanArrays.copyOf()
Ambas versiones evitan crear List
objetos intermedios y usarSystem.arraycopy()
para garantizar que copiar matrices grandes sea lo más rápido posible.
Para dos matrices se ve así:
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;
}
Y para un número arbitrario de matrices (> = 1) se ve así:
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
(y pierda el <T>
).
ByteBuffer buffer = ByteBuffer.allocate(array1.length + array2.length); buffer.put(array1); buffer.put(array2); return buffer.array();
concat(ai, ad)
, dónde ai
está Integer[]
y dónde ad
está Double[]
. (En este caso, el compilador <T>
resuelve el parámetro de tipo <? extends Number>
). La matriz creada por Arrays.copyOf
tendrá el tipo de componente de la primera matriz, es decir, Integer
en este ejemplo. Cuando la función está a punto de copiar la segunda matriz, se ArrayStoreException
lanzará un . La solución es tener un Class<T> type
parámetro adicional .
Utilizando Stream
en Java 8:
String[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b))
.toArray(String[]::new);
O así, usando flatMap
:
String[] both = Stream.of(a, b).flatMap(Stream::of)
.toArray(String[]::new);
Para hacer esto para un tipo genérico, debe usar la reflexión:
@SuppressWarnings("unchecked")
T[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b)).toArray(
size -> (T[]) Array.newInstance(a.getClass().getComponentType(), size));
.boxed()
por lo que son de tipo Stream
más bien que por ejemplo, IntStream
que no se pueden pasar como un parámetro a Stream.concat
.
a
y b
son int[]
, el usoint[] both = IntStream.concat(Arrays.stream(a), Arrays.stream(b)).toArray();
System.arrayCopy
. Pero tampoco particularmente lento. Es probable que tenga que hacer esto una muy muchas veces con grandes matrices en realidad contextos sensibles al rendimiento de la diferencia de tiempo de ejecución a la materia.
O con la amada guayaba :
String[] both = ObjectArrays.concat(first, second, String.class);
Además, hay versiones para matrices primitivas:
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)
Puede agregar las dos matrices en dos líneas de código.
String[] both = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, both, first.length, second.length);
Esta es una solución rápida y eficiente y funcionará para tipos primitivos, así como los dos métodos involucrados están sobrecargados.
Debe evitar las soluciones que involucran ArrayLists, streams, etc., ya que necesitarán asignar memoria temporal sin ningún propósito útil.
Debe evitar los for
bucles para matrices grandes, ya que no son eficientes. Los métodos incorporados utilizan funciones de copia en bloque que son extremadamente rápidas.
Usando la API de 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])
será más rápido que both.toArray(new String[both.size()])
, incluso si contradice nuestra intuición ingenua. Por eso es tan importante medir el rendimiento real al optimizar. O simplemente use la construcción más simple, cuando no se pueda probar la ventaja de la variante más complicada.
Una solución 100% antigua de Java y sin ella System.arraycopy
(no disponible en el cliente GWT, por ejemplo):
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
cheques. Y quizás establezca algunas de sus variables en final
.
null
comprobaciones de @TrippKinetics ocultarían los NPE en lugar de mostrarlos y el uso de final para vars locales no tiene ningún beneficio (todavía).
Recientemente he luchado contra problemas con la rotación excesiva de memoria. Si se sabe que ayb están comúnmente vacíos, aquí hay otra adaptación del código de silvertab (también generado):
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;
}
Editar: una versión anterior de esta publicación declaró que la reutilización de matrices como esta debe documentarse claramente. Como señala Maarten en los comentarios, en general sería mejor simplemente eliminar las declaraciones if, anulando así la necesidad de tener documentación. Pero, de nuevo, esas declaraciones if fueron el punto central de esta optimización particular en primer lugar. Dejaré esta respuesta aquí, ¡pero ten cuidado!
System.arraycopy
copia el contenido de la matriz?
if
declaraciones fuera sería la solución más fácil.
La biblioteca funcional de Java tiene una clase de envoltorio de matriz que equipa las matrices con métodos útiles como la concatenación.
import static fj.data.Array.array;
...y entonces
Array<String> both = array(first).append(array(second));
Para recuperar la matriz desenvuelta, llame a
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()])
Aquí hay una adaptación de la solución de silvertab, con genéricos reacondicionados:
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: Vea la respuesta de Joachim para una solución Java 6. No solo elimina la advertencia; ¡también es más corto, más eficiente y más fácil de leer!
Si lo usa de esta manera, no necesita importar ninguna clase de terceros.
Si quieres concatenar String
Código de muestra para concatenar dos 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;
}
Si quieres concatenar Int
Código de muestra para concatar dos matrices enteras
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;
}
Aquí está el método principal
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));
}
}
Podemos usar de esta manera también.
Perdóname por agregar otra versión a esta lista ya larga. Miré cada respuesta y decidí que realmente quería una versión con solo un parámetro en la firma. También agregué algunas comprobaciones de argumentos para beneficiarse de una falla temprana con información sensible en caso de entrada inesperada.
@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;
}
Puede intentar convertirlo en una Arraylist y usar el método addAll y luego volver a convertirlo en una matriz.
List list = new ArrayList(Arrays.asList(first));
list.addAll(Arrays.asList(second));
String[] both = list.toArray();
Aquí una posible implementación en el código de trabajo de la solución de pseudocódigo escrita por silvertab.
Gracias 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;
}
}
El siguiente es la interfaz del generador.
Nota: Un generador es necesario porque en Java no es posible hacer
new T[size]
debido a la eliminación de tipo genérico:
public interface ArrayBuilderI<T> {
public T[] build(int size);
}
Aquí un constructor concreto implementando la interfaz, construyendo una Integer
matriz:
public class IntegerArrayBuilder implements ArrayBuilderI<Integer> {
@Override
public Integer[] build(int size) {
return new Integer[size];
}
}
Y finalmente la aplicación / prueba:
@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]);
}
}
¡Guauu! Aquí hay muchas respuestas complejas, incluidas algunas simples que dependen de dependencias externas. ¿Qué tal hacerlo así:
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]);
Esto funciona, pero debe insertar su propia comprobación de errores.
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();
}
}
Probablemente no sea el más eficiente, pero no se basa en otra cosa que no sea la propia API de Java.
for
bucle con eso: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)
para omitir el primer for
bucle. Ahorra tiempo proporcional al tamaño de arr1
.
Esta es una función convertida para una matriz de cadenas:
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;
}
¿Qué tal simplemente
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());
}
}
Y solo hazlo Array.concat(arr1, arr2)
. Mientras arr1
yarr2
sean del mismo tipo, esto le dará otra matriz del mismo tipo que contiene ambas matrices.
Una versión estática genérica que utiliza System.arraycopy de alto rendimiento sin requerir una anotación @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;
}
Aquí está mi versión ligeramente mejorada del concatAll de Joachim Sauer. Puede funcionar en Java 5 o 6, utilizando System.arraycopy de Java 6 si está disponible en tiempo de ejecución. Este método (en mi humilde opinión) es perfecto para Android, ya que funciona en Android <9 (que no tiene System.arraycopy) pero utilizará el método más rápido si es posible.
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;
}
Otra forma de pensar sobre la pregunta. Para concatenar dos o más matrices, uno debe hacer una lista de todos los elementos de cada matriz y luego construir una nueva matriz. Esto suena como crear un List<T>
y luego lo invoca toArray
. Algunas otras respuestas usan ArrayList
, y eso está bien. Pero, ¿qué tal si implementamos el nuestro? No es dificil:
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);
}
Creo que lo anterior es equivalente a las soluciones que utiliza System.arraycopy
. Sin embargo, creo que este tiene su propia belleza.
Qué tal si :
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()]);
}
Una variación simple que permite la unión de más de una matriz:
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()]);
}
Usando solo la propia 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]);
}
Ahora, este código no es el más eficiente, pero se basa solo en clases estándar de Java y es fácil de entender. Funciona para cualquier número de String [] (incluso cero arrays).