我需要连接String
Java中的两个数组。
void f(String[] first, String[] second) {
String[] both = ???
}
最简单的方法是什么?
array1 + array2
串联。
我需要连接String
Java中的两个数组。
void f(String[] first, String[] second) {
String[] both = ???
}
最简单的方法是什么?
array1 + array2
串联。
Answers:
我从旧的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
将引发an 。解决方案是有一个附加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
are 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);
这是一种快速有效的解决方案,将适用于原始类型以及所涉及的两种方法都已重载。
您应避免使用涉及ArrayList,流等的解决方案,因为这些将需要分配临时内存,而无用。
您应该避免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()]);
}
溶液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
检查将隐藏NPE,而不是显示它们,并且将final用于本地var没有任何好处(尚未)。
我最近与过多的内存轮换问题进行了斗争。如果已知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;
}
编辑:这篇文章的先前版本指出,这样的数组重用应明确记录在案。正如Maarten在评论中指出的那样,通常最好删除if语句,这样就不需要文档。但是话又说回来,那些if语句首先是特定优化的重点。我会在这里留下这个答案,但要小心!
System.arraycopy
复制数组的内容?
if
语句将是最简单的解决方法。
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;
}
注意:有关Java 6解决方案的信息,请参见Joachim的答案。它不仅消除了警告;它更短,更有效且更易于阅读!
如果您使用这种方式,则无需导入任何第三方类。
如果要串联 String
连接两个字符串数组的示例代码
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();
}
}
它可能不是最有效的,但是除了Java自己的API外,它不依赖任何其他东西。
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
类型相同,这将为您提供另一个包含两个数组的相同类型的数组。
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;
}
这是我对Joachim Sauer的concatAll的稍加改进的版本。如果运行时可用,它可以使用Java 6的System.arraycopy在Java 5或6上运行。此方法(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
。但是我认为这有它自己的美丽。
仅使用Java自己的API:
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 [](甚至零个数组)。