二維數組使用Arrays.copyOf帶來的思維誤區

1.問題描述

今天在一個java學習羣裏偶然看到一段代碼,如下所示。
public static void main(String[] args) {
		int data [][] ={{1,2,3},{4,5,6},{7,8,9}};
		int dataTest[][];
		dataTest = Arrays.copyOf(data, data.length);
		dataTest[0][0] = data[2][2];			
		for (int i = 0; i < data.length; i++) {
			System.out.println(Arrays.toString(data[i]));
		}
	}

問題的發起人提出了疑問,打印結果是什麼,爲什麼會這樣?原理是什麼。
一開始我簡單看了下,沒弄清楚他要問的具體是什麼。簡單看了下,不就是複製了個二維數組,然後修改了下元素值嘛。但是他的意思卻是,爲什麼明明修改的是dataTest裏的值,但是打印data裏元素時也發生了改變?
這段代碼的運行結果如下圖:

一時間,我也沒反應過來,陷入了思維誤區,然後想來想去,難不成是Arrays.copyOf這個方法的問題?於是就去翻看了Arrays.copyOf的源碼。

2.解決過程

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
於是我按照源碼,將這個案例改寫了如下所示
	public static void main(String[] args) {
		int data [][] ={{1,2,3},{4,5,6},{7,8,9}};
		int copy[][];
		System.out.println((Object)data.getClass() == (Object)Object[].class);
		copy = (int[][]) Array.newInstance(data.getClass().getComponentType(), 3);
        System.arraycopy(data, 0, copy, 0,Math.min(data.length, 3));		
		for (int i = 0; i < data.length; i++) {
			System.out.println(Arrays.toString(data[i]));
		}
	}
源碼的大概意思是:先取得數組的Class對象,判斷這個數組是否是Object類型的數組,如果是的話就創建個新的Object類型的數組,否則的話調用newInstance方法創建相同類型的數組。然後調用System.arrayCopy方法將原數組中的元素複製到新的數組中,並最終返回。

所以說,其實兩個二維數組只是本身的地址值不一樣,但是其中的元素是一樣的。之所以會出現上面的思維誤區,是因爲忘了二維數組中存放的是一維數組的引用。

畫個簡單的內存圖來看:


調用dataTest[0][0]=data[2][2],其實是修改了0x101數組中的元素值。data和dataTest中第一個元素指向的都是0x101這塊區域。所以最終打印data數組,值也發生了改變,出現了上述的思維誤區。

3.總結

其實出這個問題的最主要原因還是沒搞清楚二維數組中存放的是每個一維數組的引用,而不是具體的元素值。不過通過這次小問題也看了看Arrays類中copyOf方法的源碼,也算有一些收穫。有時候學習就會陷入一種誤區,知識點一綜合,感覺被新的方法、新的技術繞進去了,但仔細想想,問題的本質出在哪兒,可能就是你忽略了的基礎而已。。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章