Eu preciso concatenar duas String
matrizes em Java.
void f(String[] first, String[] second) {
String[] both = ???
}
Qual é a maneira mais fácil de fazer isso?
array1 + array2
concatenação simples .
Eu preciso concatenar duas String
matrizes em Java.
void f(String[] first, String[] second) {
String[] both = ???
}
Qual é a maneira mais fácil de fazer isso?
array1 + array2
concatenação simples .
Respostas:
Encontrei uma solução de uma linha na boa e antiga biblioteca Apache Commons Lang.
ArrayUtils.addAll(T[], T...)
Código:
String[] both = ArrayUtils.addAll(first, second);
Aqui está um método simples que concatenará duas matrizes e retornará o 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;
}
Observe que ele não funcionará com tipos de dados primitivos, apenas com tipos de objetos.
A seguinte versão um pouco mais complicada funciona com matrizes de objeto e primitivas. Faz isso usando em T
vez de T[]
como o tipo de argumento.
Também possibilita concatenar matrizes de dois tipos diferentes, escolhendo o tipo mais geral como o tipo de componente do 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;
}
Aqui está um exemplo:
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);
. Surpreendentemente, nunca vi isso antes. @ Beaudet Acho que a anotação está bem aqui, considerando por que está sendo suprimida.
É possível escrever uma versão totalmente genérica que pode até ser estendida para concatenar qualquer número de matrizes. Essas versões requerem Java 6, pois usamArrays.copyOf()
Ambas as versões evitam a criação de List
objetos intermediários e são usadas System.arraycopy()
para garantir que a cópia de matrizes grandes seja o mais rápida possível.
Para duas matrizes, fica assim:
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 para um número arbitrário de matrizes (> = 1), fica assim:
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
por byte
(e perder o <T>
).
ByteBuffer buffer = ByteBuffer.allocate(array1.length + array2.length); buffer.put(array1); buffer.put(array2); return buffer.array();
concat(ai, ad)
, onde ai
está Integer[]
e ad
está Double[]
. (Nesse caso, o parâmetro type <T>
é resolvido <? extends Number>
pelo compilador.) A matriz criada por Arrays.copyOf
terá o tipo de componente da primeira matriz, ou seja Integer
, neste exemplo. Quando a função está prestes a copiar a segunda matriz, um ArrayStoreException
será lançado. A solução é ter um Class<T> type
parâmetro adicional .
Usando Stream
no Java 8:
String[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b))
.toArray(String[]::new);
Ou assim, usando flatMap
:
String[] both = Stream.of(a, b).flatMap(Stream::of)
.toArray(String[]::new);
Para fazer isso para um tipo genérico, você precisa usar a reflexão:
@SuppressWarnings("unchecked")
T[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b)).toArray(
size -> (T[]) Array.newInstance(a.getClass().getComponentType(), size));
.boxed()
modo que sejam do tipo Stream
e não, por exemplo, IntStream
que não possam ser passados como parâmetro para Stream.concat
.
a
e b
são int[]
, usoint[] both = IntStream.concat(Arrays.stream(a), Arrays.stream(b)).toArray();
System.arrayCopy
. Mas também não é particularmente lento. Você provavelmente tem que fazer isso uma muito muitas vezes com enormes matrizes em realmente contextos sensíveis de desempenho para a diferença de tempo de execução para a matéria.
Ou com a amada Goiaba :
String[] both = ObjectArrays.concat(first, second, String.class);
Além disso, existem versões para matrizes 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)
Você pode anexar as duas matrizes em duas linhas de código.
String[] both = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, both, first.length, second.length);
Esta é uma solução rápida e eficiente e funcionará para tipos primitivos, bem como os dois métodos envolvidos estão sobrecarregados.
Você deve evitar soluções que envolvam ArrayLists, fluxos etc., pois elas precisarão alocar memória temporária sem nenhum objetivo útil.
Você deve evitar for
loops para matrizes grandes, pois elas não são eficientes. Os métodos integrados usam funções de cópia em bloco extremamente rápidas.
Usando a 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])
será mais rápido do que both.toArray(new String[both.size()])
, mesmo que contradiga nossa intuição ingênua. É por isso que é tão importante medir o desempenho real ao otimizar. Ou apenas use a construção mais simples, quando a vantagem da variante mais complicada não puder ser comprovada.
Uma solução java 100% antiga e sem System.arraycopy
(não disponível no cliente GWT, por exemplo):
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. E talvez defina algumas de suas variáveis para final
.
null
verificações do @TrippKinetics ocultariam as NPEs , em vez de mostrá-las, e o uso final de vars locais ainda não tem nenhum benefício.
Recentemente, lutei contra problemas com rotação excessiva de memória. Se se sabe que a e / ou b geralmente estão vazios, aqui está outra adaptação do código do silvertab (também gerado):
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: Uma versão anterior deste post afirmou que a reutilização da matriz como esta deve ser claramente documentada. Como Maarten aponta nos comentários, em geral, seria melhor remover as declarações if, anulando assim a necessidade de ter documentação. Mas, novamente, essas instruções if foram o ponto principal dessa otimização específica em primeiro lugar. Vou deixar essa resposta aqui, mas tenha cuidado!
System.arraycopy
copia o conteúdo da matriz?
if
declarações de fora seria a solução mais fácil.
A biblioteca Java Funcional possui uma classe de wrapper de matriz que equipa as matrizes com métodos úteis, como concatenação.
import static fj.data.Array.array;
...e depois
Array<String> both = array(first).append(array(second));
Para recuperar a matriz desembrulhada, chame
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()])
;)
Aqui está uma adaptação da solução da silvertab, com os genéricos adaptados:
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: Consulte a resposta de Joachim para obter uma solução Java 6. Não apenas elimina o aviso; também é mais curto, mais eficiente e mais fácil de ler!
Se você usar dessa maneira, não precisará importar nenhuma classe de terceiros.
Se você quer concatenar String
Código de exemplo para 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;
}
Se você quer concatenar Int
Código de exemplo para 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;
}
Aqui está o 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));
}
}
Também podemos usar esse caminho.
Por favor, perdoe-me por adicionar mais uma versão a esta lista já longa. Olhei para todas as respostas e decidi que realmente queria uma versão com apenas um parâmetro na assinatura. Também adicionei uma verificação de argumento para se beneficiar de falhas precoces com informações sensatas em 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;
}
Você pode tentar convertê-lo em uma lista de matriz e usar o método addAll e depois converter novamente em uma matriz.
List list = new ArrayList(Arrays.asList(first));
list.addAll(Arrays.asList(second));
String[] both = list.toArray();
Aqui está uma possível implementação no código de trabalho da solução de pseudocódigo escrita por silvertab.
Obrigado 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;
}
}
A seguir, é apresentada a interface do construtor.
Nota: Um construtor é necessário porque em java não é possível fazer
new T[size]
devido ao apagamento de tipo genérico:
public interface ArrayBuilderI<T> {
public T[] build(int size);
}
Aqui, um construtor de concreto implementando a interface, construindo uma Integer
matriz:
public class IntegerArrayBuilder implements ArrayBuilderI<Integer> {
@Override
public Integer[] build(int size) {
return new Integer[size];
}
}
E finalmente a aplicação / teste:
@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]);
}
}
Uau! Muitas respostas complexas aqui, incluindo algumas simples que dependem de dependências externas. Que tal fazer assim:
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]);
Isso funciona, mas você precisa inserir sua própria verificação de erro.
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();
}
}
Provavelmente não é o mais eficiente, mas não depende de nada além da própria API do Java.
for
loop por aquele: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 pular o primeiro for
loop. Economiza tempo proporcional ao tamanho de arr1
.
Esta é uma função convertida para uma matriz 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 tal simplesmente
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 apenas faça Array.concat(arr1, arr2)
. Contanto que arr1
e arr2
sejam do mesmo tipo, isso fornecerá outra matriz do mesmo tipo contendo as duas matrizes.
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;
}
Aqui está minha versão ligeiramente melhorada do concatAll de Joachim Sauer. Ele pode funcionar no Java 5 ou 6, usando System.arraycopy do Java 6, se estiver disponível em tempo de execução. Esse método (IMHO) é perfeito para o Android, pois funciona no Android <9 (que não possui System.arraycopy), mas utilizará o método mais rápido, se possível.
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;
}
Outra maneira de pensar sobre a questão. Para concatenar duas ou mais matrizes, é necessário listar todos os elementos de cada matriz e criar uma nova matriz. Parece criar um List<T>
e, em seguida, chama toArray
. Algumas outras respostas usam ArrayList
, e isso é bom. Mas que tal implementar a nossa? Não é difícil:
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);
}
Eu acredito que o acima é equivalente a soluções que usa System.arraycopy
. No entanto, acho que este tem sua própria beleza.
E se :
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()]);
}
Uma variação simples que permite a junção de mais de uma 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 apenas a API própria do 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]);
}
Agora, esse código não é o mais eficiente, mas depende apenas de classes java padrão e é fácil de entender. Funciona para qualquer número de String [] (mesmo zero matrizes).