Java合併數組的兩種實現方式

最近在寫代碼時遇到了需要合併兩個數組的需求,突然發現以前沒用過,於是研究了一下合併數組的方式,總結如下。

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

發佈了95 篇原創文章 · 獲贊 99 · 訪問量 41萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章