Java合并数组的两种实现方式

本文介绍两种Java中合并数组的方法:System.arraycopy()和ArrayUtils.addAll()。System.arraycopy()直接操作数组,ArrayUtils.addAll()则借助Apache Commons Lang库简化操作。文章还分享了在使用ArrayUtils.addAll()方法时常见的错误及解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在写代码时遇到了需要合并两个数组的需求,突然发现以前没用过,于是研究了一下合并数组的方式,总结如下。

1.System.arraycopy()方法

(1) 解析
通过阅读JDK源码,我可以知道方法原型如下:

public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

其中:
src是源数组
srcPos是源数组复制的起始位置
dest是目标数组
destPos是目标数组接收复制数据的起始位置
length是复制的长度(源数组中从复制起始位置srcPos开始需要复制的长度)

可以看到,该方法是本地方法,我们不能更深一步的了解其中的原理,但是我们可以知道其作用就是将源数组从起始位置srcPos开始将length长度的元素复制到目标数组,目标数组从destPos位置开始接收复制元素。

(2) 示例

String[] aa = {"11","22","33"};
String[] bb = {"44","55","66"};
String[] cc = {"77","88","99"};

// 合并两个数组
String[] dd = new String[aa.length + bb.length];
System.arraycopy(aa, 0, dd, 0, aa.length);
System.arraycopy(bb, 0, dd, aa.length, bb.length);

// 合并三个数组
String[] ee = new String[aa.length + bb.length + cc.length];
System.arraycopy(aa, 0, ee, 0, aa.length);
System.arraycopy(bb, 0, ee, aa.length, bb.length);
System.arraycopy(cc, 0, ee, aa.length + bb.length, cc.length);

2.ArrayUtils.addAll()方法

(1) 解析
ArrayUtils工具类在apache commons-lang3-3.5中的commons-lang3-3.5.jar(jar包下载地址)中,所以使用之前需要导入这个包。通过阅读其源码,我们可以发现,其实addAll()方法本质上还是调用System.arraycopy()方法。

public static <T> T[] addAll(final T[] array1, final T... array2) {
     if (array1 == null) {
         return clone(array2);
     } else if (array2 == null) {
         return clone(array1);
     }
     final Class<?> type1 = array1.getClass().getComponentType();
     @SuppressWarnings("unchecked") // OK, because array is of type T
     final
     // a处
     T[] joinedArray = (T[]) Array.newInstance(type1, array1.length + array2.length);
     System.arraycopy(array1, 0, joinedArray, 0, array1.length);
     try {
         // b处
         System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
     } catch (final ArrayStoreException ase) {
         // Check if problem was due to incompatible types
         /*
          * We do this here, rather than before the copy because:
          * - it would be a wasted check most of the time
          * - safer, in case check turns out to be too strict
          */
         final Class<?> type2 = array2.getClass().getComponentType();
         if (!type1.isAssignableFrom(type2)) {
             throw new IllegalArgumentException("Cannot store " + type2.getName() + " in an array of "
                     + type1.getName(), ase);
         }
         throw ase; // No, so rethrow original
     }
     return joinedArray;
 }

这个方法关键的两个部分就是代码中标注的a、b两处。a处首先通过反射的方式定义了一个长度为array1、array2长度之和的数组joinedArray,然后将array1拷贝到joinedArray,b处将array2拷贝到joinedArray中。除了这两处,还有异常处理需要注意,在捕获异常后,判断了type1是不是继承于type2,也就是判断array1所对应的类型是否与array2对应类型相同,或者array1所对应的类型是否是array2对应类型的子类,否则将出现不兼容的情况。

(2) 示例

// 合并两个数组
String[] ff = ArrayUtils.addAll(aa, bb);

// 将多个字符串合并到数组构成新数组
String[] gg = ArrayUtils.addAll(aa, "12", "13");
String[] hh = ArrayUtils.addAll(aa, "12", "13", "14");
String[] ii = ArrayUtils.addAll(aa, "12", "13", "14", "15");

// 合并三个数组
String[] jj =  ArrayUtils.addAll(aa, bb);
String[] kk =  ArrayUtils.addAll(jj, cc);

需要说明的是,addAll()的第二个参数是可变参数,可以传入多个相同类型的值,或者一个该类型的数组。

(3) 错误处理
我在使用过程中遇到一个问题,具体如下:
代码1:

String[] ll =  ArrayUtils.addAll(aa, bb, cc);

根据提示发现,addAll()返回值类型是Serializable[],所有不能喝String[]兼容。于是我将代码改成如下代码:
代码2:

ArrayUtils.addAll(aa, bb, cc);

我以为不接收返回值只是合并就不会报错了,虽然通过了编译,但是运行还是报错了。错误信息如下:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot store java.io.Serializable in an array of java.lang.String
    at org.apache.commons.lang3.ArrayUtils.addAll(ArrayUtils.java:5084)
    at com.liu.date20170524.MergeArrays.main(MergeArrays.java:44)
Caused by: java.lang.ArrayStoreException
    at java.lang.System.arraycopy(Native Method)
    at org.apache.commons.lang3.ArrayUtils.addAll(ArrayUtils.java:5073)
    ... 1 more

由于当时陷入了惯性思维,认为addAll和List等的addAll()方法类型,可以将多个列表(这里是数组)进行合并,所以一直没想明白错误出在哪里。后来才发现,addAll()的第二个参数的类型是T,不是T[],所以不能合并两个以上的数组。望各位引以为戒。

【参考】http://blog.csdn.net/jaycee110905/article/details/9179227

<think>好的,用户想知道在Java中如何合并数组。我需要根据提供的引用内容来回答,并且按照系统级指令来组织内容。首先,系统要求使用中文回答,正确应用LaTeX格式的数学表达式,生成相关问题,并在引用处添加标识。 首先,用户的问题是关于合并数组的方法。引用[1]提到了四种方法:arraycopy()、不用arraycopy()、集合和Stream API。我需要逐一解释这四种方法,并给出代码示例。同时,引用[2]提到了合并两个已排序数组且不使用额外内存的情况,这可能涉及双指针法,需要补充这部分内容。引用[3]和[4]是关于数组合并排序的示例,但用户的问题更关注合并而非排序,所以可能重点放在前两种引用上。 接下来,我需要确保每个方法都有对应的代码示例,并正确引用来源。例如,在讲解arraycopy()方法时,引用[1],而在讲解双指针合并有序数组时引用[2]。同时,注意代码块的格式是否正确,使用正确的Java语法。 另外,系统要求生成相关问题,我需要根据合并数组的不同方面来设计问题,比如不同方法的比较、内存优化、处理已排序数组等。这些问题需要与用户的问题相关,并帮助用户进一步学习。 还要检查是否所有数学表达式都用$...$或$$...$$格式,比如在时间复杂度中提到$O(n)$,但用户的问题可能不涉及数学公式,所以这部分可能较少。但如果有提到算法复杂度,就需要正确应用格式。 最后,确保回答结构清晰,分步骤说明每种方法,并在末尾添加相关问题。同时,所有引用标识要自然添加在对应的段落末尾,如[^1]、[^2]等。</think>在Java合并数组主要有以下几种方法,根据不同的需求和场景可以选择合适的方式: ### 一、使用`System.arraycopy()`方法 这是最高效的原生数组合并方法,直接操作内存空间: ```java int[] arr1 = {1,2,3}; int[] arr2 = {4,5,6}; int[] merged = new int[arr1.length + arr2.length]; System.arraycopy(arr1, 0, merged, 0, arr1.length); // 从merged数组的0位置开始拷贝 System.arraycopy(arr2, 0, merged, arr1.length, arr2.length); ``` 时间复杂度为$O(n)$,适合大数据量场景[^1] ### 二、手动循环实现 适用于需要自定义合并逻辑的情况: ```java int[] merged = new int[arr1.length + arr2.length]; for(int i=0; i<arr1.length; i++){ merged[i] = arr1[i]; } for(int i=0; i<arr2.length; i++){ merged[arr1.length + i] = arr2[i]; } ``` ### 三、使用集合框架(Collections) 适合需要动态调整数组大小的情况: ```java List<Integer> list = new ArrayList<>(Arrays.asList(arr1)); list.addAll(Arrays.asList(arr2)); Integer[] mergedArray = list.toArray(new Integer[0]); ``` ### 四、Java Stream API(JDK8+) 函数式编程风格的实现: ```java int[] merged = IntStream.concat( Arrays.stream(arr1), Arrays.stream(arr2) ).toArray(); ``` ### 五、合并有序数组的特殊处理 对于已排序数组合并,可以使用双指针法实现$O(n)$时间复杂度,且不需要额外空间(当第二个数组预留足够空间时)[^2]: ```java public static void mergeSorted(int[] nums1, int m, int[] nums2, int n) { int p1 = m-1, p2 = n-1, p = m+n-1; while (p2 >= 0) { nums1[p--] = (p1 >=0 && nums1[p1] > nums2[p2]) ? nums1[p1--] : nums2[p2--]; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值