2013年11月5日 星期二

ArrayList 使用 toarray 轉換成物件陣列

     在某些情況下,我們希望能夠將 ArrayList 物件所存的內容轉換成陣列的形式,在 ArrayList 中提供了 toarray 方法來達到我們的需求。下面是 Java 所提供的 toarray 方法的原始碼:

    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

    public T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

     有上面可知,我們有兩個方式可以使用 toarray 方法,一個是不帶任何參數 toarray 方法回傳 Object 陣列,另外一個是回傳你所傳入的型態陣列。
     我們先來看 public T[] toArray(T[] a) 的方法,從原始碼中,我們發現此方法會使用參數的 length 的屬性,代表該參數必須要先配置過記憶體才行,如果直接傳 null 給它會引發 "java.lang.NullPointerException" 的例外事件。再回頭看一下原始碼,會發現當你所配置的陣列元素小於目前 ArrayList 所儲存的元素時,toarray 方法會幫你配置足夠的元素來儲存目前 ArrayList 物件中所含的所有元素。而當你的所配置的陣列元素個數大於目前會將你所配置的第 size 元素設定為null。基本上你可以想成超出的元素都會被設置成 null 就可以。(PS: 要使用 ArrayList 其型態一定是參考型態,如果使用基本型態會出現編譯失敗 ,而物件陣列的預設值為 null)。

    另外一個 toArray法是回傳 Object 陣列,由於兩個陣列基底型別不同時,不能互相參照,所以你無法直接將回傳的 Object 陣列轉成你要的型態陣列。但是你可以針對每個 Object 元素做轉型。

範例程式如下:

     static void toArrayDemo() {
        
        ArrayList<integer> list = new ArrayList<integer>(){{
            add(1);
            add(2);
            add(3);
            add(4);
        }};

        //"main" java.lang.NullPointerException
        //Integer[] test = null; 
        //test = list.toArray(test); 
        // or Integer[] test = list.toArray(null);
        
        // 配置的元素小於 ArrayList 所存的元素各數少時,
        // toArray 會幫你重新配置足夠的陣列個數來儲存。
        Integer[] test = list.toArray(new Integer[0]);
        System.out.print("<1>[ ");
        for (Integer val : test) {
            System.out.print(val + " ");
        }
        System.out.println("]");
        // 配置大於 ArrayList 所存的 元素個數,多餘的陣列元素都是 null
        Integer[] test2 = list.toArray(new Integer[10]);
        System.out.print("<2>[ ");
        for (Integer val : test2) {
            System.out.print(val + " ");
        }
        
        System.out.println("]");
        
        // 配置符合目前 ArrayList 所含的元素個數
        Integer[] test3 = list.toArray(new Integer[list.size()]);
        System.out.print("<3>[ ");
        for (Integer val : test3) {
            System.out.print(val + " ");
        }
        System.out.println("]");
        
        //無法將 Object 陣列轉成其他型態的陣列
        //[Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
        //Integer[] test4 = (Integer[] )list.toArray();
        
        Object [] obj = list.toArray();
        
        System.out.print("<4>[ ");
        for (Object val : obj) {
            // 你可以對每一個 Object 中的元素轉型成 Integer 型態的物件
            // 因為我們宣告的 ArrayList 是儲存 Integer 物件,所以每一個
            // Object 物件都是參考到  Integer 物件
            
            System.out.print( ((Integer)val).intValue() + " ");
            // 下面方式與上面轉型所列印出來的結果相同,因為每一個 Object 元素
            // 都是參考到 Integer 物件,而 Integer 的 toString method 就是列印其值。
            //System.out.print( val + " ");
        }
        System.out.println("]");
    }

輸出結果:
<1>[ 1 2 3 4 ]
<2>[ 1 2 3 4 null null null null null null ]
<3>[ 1 2 3 4 ]
<4>[ 1 2 3 4 ]

補充:
    Object[] obj = new Object[size] 與 Object[] intObj = new Integer[2]  的不同是,obj 陣列可以指定
任何型態的物件參考,每一個元素值不一定要儲存相同的物件。而 intObj 是參考到 Integer 陣列所以其元素只能儲存 Integer 物件,儲存其他型態物件會引發 "java.lang.ArrayStoreException" 例外事件。另外 intObj 是參考到 Integer 陣列,所以你可以將 intObj 指定給其他 Integer 陣列來參考。

範例程式如下:

    static void arrayDemo() {
        Object [] intobj = new Integer[2];
        System.out.println("obj instance Integer[] = " + (intobj instanceof Integer[]));
        obj[0] = new Integer(10);
        //Exception in thread "main" java.lang.ArrayStoreException: java.lang.Float
        //obj[1] = new Float(10.f);
        
        Object [] obj2 = new Object[2];
        System.out.println("obj2 instance Integer[] = " + (obj2 instanceof Integer[]));
        obj2[0] = new Integer(10);
        obj2[1] = new Float(10.f);
        System.out.println("obj2[0] = " + obj2[0] + " , obj2[1] = " + obj2[1]);
    }

沒有留言: