题 如何在Java中连接两个数组?


我需要在Java中连接两个String数组。

void f(String[] first, String[] second) {
    String[] both = ???
}

最简单的方法是什么?


1136


起源


来自番石榴的Bytes.concat - Ben Page
我在这里看到很多回复,但问题是如此措辞('最简单的方法'?),它不允许表明最佳答案...... - Artur Opalinski
这里有数十个答案是将数据复制到一个新的数组中,因为这就是所要求的 - 但是在非严格必要时复制数据是一件坏事,特别是在Java中。相反,跟踪索引并使用两个数组,就像它们已连接一样。我添加了说明该技术的解决方案。 - Douglas Held
最简单的是你可能不应该首先使用数组,你应该使用ArrayLists,你的输出应该是一个ArrayList。一旦你完成了这些前置条件,操作就会内置 - first.addAll(second)。这种情况不是很自动的唯一情况是你的数组是非对象类型(int,long,double,...),在这种情况下,内部数组可以比ArrayLists有更大的优势 - 但对于字符串--meh - Bill K
事实上这样的问题目前有50个不同的答案让我想知道为什么Java从来没有变得简单 array1 + array2 级联。 - JollyJoker


答案:


我从优秀的旧Apache Commons Lang库中找到了一个单行解决方案。
  ArrayUtils.addAll(T[], T...)

码:

String[] both = (String[])ArrayUtils.addAll(first, second);

923



我不知道,这有点作弊。我想大多数人可能不会想要这种方法的额外依赖。 - Outlaw Programmer
如果回答这个问题,它是如何“作弊”的?当然,对于这种特定情况,有一个额外的依赖可能是过度的,但是在调用它存在时没有任何损害,特别是因为Apache Commons中有如此多的优秀功能。 - Rob
我同意,这并没有真正回答这个问题。高级库可能很棒,但如果您想学习一种有效的方法,那么您需要查看库方法正在使用的代码。此外,在许多情况下,您不能只是通过产品中的另一个库。 - AdamC
我认为这是一个很好的答案。 POJO解决方案也已经提供,但如果OP已经在他们的程序中使用Apache Commons(完全可以考虑它的受欢迎程度),他可能仍然不知道这个解决方案。然后,他不会“为这一种方法添加依赖项”,而是会更好地利用现有的库。 - Adam
如果您总是担心不为单个方法添加库,则不会添加任何新库。鉴于Apache Commons中出现的优秀实用程序,我强烈建议在第一个用例出现时添加它。 - Hindol


这是一个简单的方法,它将连接两个数组并返回结果:

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 }));

722



我喜欢这个建议,因为它不太依赖于最新的Java版本。在我的项目中,我经常使用旧版本的Java或CLDC配置文件,其中一些像Antti提到的设施不可用。 - Kevin
以下行将破坏通用部分:concatenate(new String [] {“1”},new Object [] {new Object()}) - dragon66


可以编写一个完全通用的版本,甚至可以扩展为连接任意数量的数组。这个版本需要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;
}

440



@djBO:对于原始类型的数组,你需要为每种类型重载:只需复制代码并替换每个 T 同 byte (并失去了 <T>)。 - Joachim Sauer
你能告诉我如何在班上使用<T>操作符类型吗? - Johnydep
我会把它添加到开头,只是为了防守。 if(first == null){if(second == null){return null; } return second; } if(second == null){return first; } - marathon
@djBo:怎么样:ByteBuffer buffer = ByteBuffer.allocate(array1.length + array2.length); buffer.put(array1); buffer.put(array2); return buffer.array(); - Sam Goldberg
如果您使用不同组件类型的数组调用这些函数,则此方法中存在一个错误 concat(ai, ad),哪里 ai 是 Integer[] 和 ad 是 Double[]。 (在这种情况下,类型参数 <T> 决心 <? extends Number> 由编译器。)创建的数组 Arrays.copyOf 将具有第一个数组的组件类型,即 Integer 在这个例子中。当函数即将复制第二个数组时,一个 ArrayStoreException 将被抛出。解决方案是另外一个 Class<T> type 参数。 - T-Bull


Java 8中的单线程:

String[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b))
                      .toArray(String[]::new);

要么:

String[] both = Stream.of(a, b).flatMap(Stream::of)
                      .toArray(String[]::new);

328



这有多高效? - Supuhstar
值得一读: jaxenter.com/...  tl; dr - 溪流可能是表现与否,取决于你对它们做了什么以及问题的限制(这不总是答案吗?大声笑) - Trevor Brown
另外,如果 一个 要么 b 是原始类型的数组,它们的流将是必需的 .boxed() 所以它们属于类型 Stream 而不是例如 IntStream 不能作为参数传递给 Stream.concat。 - Will Hardwick-Smith
@Will Hardwick-Smith:不,你只需要选择正确的流类,例如如果 a 和 b 是 int[], 使用 int[] both = IntStream.concat(Arrays.stream(a), Arrays.stream(b)).toArray(); - Holger
@Supuhstar:可能没那么快 System.arrayCopy。但也不是特别慢。你可能必须这样做 非常 很多次 巨大 数组中的 真 性能敏感的上下文,执行时间差异很重要。 - Lii


或与心爱的人 番石榴

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)

168



尽管我喜欢Guava,但是Apache Commons的方法可以更好地处理nullables。 - Ravi Wallau
虽然使用库很好,但遗憾的是问题已被抽象出来了。因此,潜在的解决方案仍然难以捉摸。 - user924272
什么是抽象问题? Dunno在这里重新发明轮子的重要性是什么,如果你想了解问题,请查看源代码或阅读它。专业代码应该使用高级库,如果它是在Google内部开发的话,会更好! - Breno Salgado
@RaviWallau你能链接到这样做的课吗? - Sébastien Tromp
@SébastienTromp这是这个问题的最佳解决方案 - ArrayUtils。 - Ravi Wallau


使用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()]);
}

53



简单但效率低,因为它为ArrayList创建一个数组,然后为toArray方法生成另一个数组。但仍然有效,因为它很容易阅读。 - PhoneixS
适用于字符串和对象(如问题所需),但主要类型没有addAll方法(作为整数) - joro


一个办法 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;
}

37



我为File []重做了我的工作,但它是一样的。谢谢你的解决方案 - ShadowFlame
虽然可能效率很低。 - JonasCz
您可能想要添加 null 检查。或许可以设置一些变量 final。 - Tripp Kinetics