Мне нужно объединить два String
массива в Java.
void f(String[] first, String[] second) {
String[] both = ???
}
Какой самый простой способ сделать это?
array1 + array2
конкатенации.
Мне нужно объединить два String
массива в Java.
void f(String[] first, String[] second) {
String[] both = ???
}
Какой самый простой способ сделать это?
array1 + array2
конкатенации.
Ответы:
Я нашел однострочное решение из старой доброй библиотеки Apache Commons Lang.
ArrayUtils.addAll(T[], T...)
Код:
String[] both = ArrayUtils.addAll(first, second);
Вот простой метод, который объединит два массива и вернет результат:
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;
}
Обратите внимание, что он не будет работать с примитивными типами данных, только с типами объектов.
Следующая немного более сложная версия работает как с объектными, так и с примитивными массивами. Это делается с использованием T
вместо типа T[]
аргумента.
Это также позволяет объединять массивы двух разных типов, выбирая наиболее общий тип в качестве типа компонента результата.
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;
}
Вот пример:
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);
. Я удивительно никогда не видел этого раньше. @beaudet Я думаю, что аннотация здесь хорошо, учитывая, почему она подавляется.
Можно написать полностью универсальную версию, которая может быть расширена для объединения любого числа массивов. Эти версии требуют Java 6, так как они используютArrays.copyOf()
Обе версии избегают создания каких-либо промежуточных List
объектов и используютSystem.arraycopy()
для обеспечения максимально быстрого копирования больших массивов.
Для двух массивов это выглядит так:
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;
}
А для произвольного числа массивов (> = 1) это выглядит так:
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
на byte
(и потеряйте <T>
).
ByteBuffer buffer = ByteBuffer.allocate(array1.length + array2.length); buffer.put(array1); buffer.put(array2); return buffer.array();
concat(ai, ad)
, где ai
есть Integer[]
и ad
есть Double[]
. (В этом случае параметр типа <T>
разрешается <? extends Number>
компилятором.) Созданный массив Arrays.copyOf
будет иметь тип компонента первого массива, т.е. Integer
в этом примере. Когда функция собирается скопировать второй массив, ArrayStoreException
будет сгенерировано. Решение состоит в том, чтобы иметь дополнительный Class<T> type
параметр.
Использование Stream
в Java 8:
String[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b))
.toArray(String[]::new);
Или вот так, используя flatMap
:
String[] both = Stream.of(a, b).flatMap(Stream::of)
.toArray(String[]::new);
Чтобы сделать это для универсального типа, вы должны использовать отражение:
@SuppressWarnings("unchecked")
T[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b)).toArray(
size -> (T[]) Array.newInstance(a.getClass().getComponentType(), size));
.boxed()
такими, чтобы они имели тип, Stream
а не, например, IntStream
которые нельзя было бы передать в качестве параметра Stream.concat
.
a
и b
есть int[]
, используйтеint[] both = IntStream.concat(Arrays.stream(a), Arrays.stream(b)).toArray();
System.arrayCopy
. Но не особенно медленно. Возможно, вам придется делать это очень много раз с огромными массивами в действительно чувствительных к производительности контекстах, чтобы разница во времени выполнения имела значение.
Или с любимой гуавы :
String[] both = ObjectArrays.concat(first, second, String.class);
Также есть версии для примитивных массивов:
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)
Вы можете добавить два массива в две строки кода.
String[] both = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, both, first.length, second.length);
Это быстрое и эффективное решение, которое будет работать для примитивных типов, так как оба метода перегружены.
Вам следует избегать решений, включающих ArrayLists, потоки и т. Д., Так как они должны будут выделять временную память без какой-либо полезной цели.
Вы должны избегать for
циклов для больших массивов, так как они не эффективны. Встроенные методы используют функции блочного копирования, которые чрезвычайно быстры.
Использование 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])
будет быстрее both.toArray(new String[both.size()])
, даже если это противоречит нашей наивной интуиции. Вот почему так важно измерять реальную производительность при оптимизации. Или просто используйте более простую конструкцию, когда преимущество более сложного варианта не может быть доказано.
Решение 100% старого Java и без System.arraycopy
(недоступно, например, в клиенте 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
чеки. И, возможно, установите некоторые из ваших переменных в final
.
null
Проверки @TrippKinetics будут скрывать NPE, а не показывать их, и использование final для локальных переменных не имеет никакой пользы (пока).
Я недавно боролся с проблемами с чрезмерным вращением памяти. Если известно, что a и / или b обычно пусты, вот еще одна адаптация кода silvertab (также обобщенная):
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;
}
Редактировать: Предыдущая версия этого поста утверждала, что повторное использование массива должно быть четко задокументировано. Как указывает Мартен в комментариях, в целом было бы лучше просто удалить операторы if, что исключает необходимость иметь документацию. Но опять же, эти операторы if были главной целью этой конкретной оптимизации. Я оставлю этот ответ здесь, но будьте осторожны!
System.arraycopy
копирует содержимое массива?
if
исключение двух утверждений было бы самым простым решением.
Функциональная Java библиотека имеет класс - оболочки массива, экипирование массивов с удобными методами , как конкатенация.
import static fj.data.Array.array;
...а потом
Array<String> both = array(first).append(array(second));
Чтобы вернуть развернутый массив, вызовите
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()])
)
Вот адаптация решения silvertab с модифицированными дженериками:
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;
}
ПРИМЕЧАНИЕ. См . Ответ Joachim для решения Java 6. Это не только устраняет предупреждение; это также короче, более эффективно и легче читать!
Если вы используете этот способ, вам не нужно импортировать какие-либо сторонние классы.
Если вы хотите объединить String
Пример кода для конкатенации двух 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;
}
Если вы хотите объединить Int
Пример кода для конкатенации двух целочисленных массивов
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;
}
Вот основной метод
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));
}
}
Мы можем использовать этот способ также.
Пожалуйста, прости меня за добавление еще одной версии в этот уже длинный список. Я посмотрел на каждый ответ и решил, что мне действительно нужна версия с одним параметром в подписи. Я также добавил некоторую проверку аргументов, чтобы извлечь выгоду из раннего сбоя с разумной информацией в случае неожиданного ввода.
@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;
}
Вы можете попробовать преобразовать его в Arraylist и использовать метод addAll, а затем преобразовать обратно в массив.
List list = new ArrayList(Arrays.asList(first));
list.addAll(Arrays.asList(second));
String[] both = list.toArray();
Здесь возможна реализация в рабочем коде решения псевдокода, написанного silvertab.
Спасибо 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;
}
}
Далее следует интерфейс строителя.
Примечание: сборщик необходим, потому что в Java это невозможно сделать
new T[size]
из-за стирания универсального типа:
public interface ArrayBuilderI<T> {
public T[] build(int size);
}
Вот конкретный конструктор, реализующий интерфейс, строящий Integer
массив:
public class IntegerArrayBuilder implements ArrayBuilderI<Integer> {
@Override
public Integer[] build(int size) {
return new Integer[size];
}
}
И, наконец, приложение / тест:
@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]);
}
}
Вот Это Да! Здесь много сложных ответов, в том числе простых, которые зависят от внешних зависимостей. как насчет этого:
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]);
Это работает, но вам нужно вставить свою собственную проверку ошибок.
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();
}
}
Это, вероятно, не самый эффективный, но он не зависит ни от чего, кроме собственного API Java.
for
цикл на это: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)
чтобы пропустить первый for
цикл. Экономит время, пропорциональное размеру arr1
.
Это преобразованная функция для массива 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;
}
Как насчет просто
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());
}
}
И просто делай Array.concat(arr1, arr2)
. Пока arr1
и arr2
одного типа, это даст вам другой массив того же типа, содержащий оба массива.
Универсальная статическая версия, которая использует высокопроизводительную System.arraycopy, не требуя аннотации @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;
}
Вот моя слегка улучшенная версия concatAll Йоахима Зауэра. Он может работать на Java 5 или 6, используя Java 6 System.arraycopy, если он доступен во время выполнения. Этот метод (IMHO) идеально подходит для Android, так как он работает на Android <9 (у которого нет System.arraycopy), но будет по возможности использовать более быстрый метод.
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;
}
Еще один способ задуматься над вопросом. Чтобы объединить два или более массивов, нужно составить список всех элементов каждого массива и затем создать новый массив. Это звучит как создать, List<T>
а затем призывает toArray
его. Некоторые другие ответы использует ArrayList
, и это нормально. Но как насчет реализации нашего? Это не сложно
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);
}
Я считаю, что вышеупомянутое эквивалентно решениям, которые используют System.arraycopy
. Однако я думаю, что у этого есть своя собственная красота.
Как насчет :
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()]);
}
Простой вариант, позволяющий объединять более одного массива:
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()]);
}
Используя только собственный API 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]);
}
Теперь этот код не самый эффективный, но он опирается только на стандартные классы Java и прост для понимания. Это работает для любого числа String [] (даже нулевых массивов).