Array 和 List 的转换问题
Array 和 List 都是我們?cè)陂_(kāi)發(fā)過(guò)程中常見(jiàn)的數(shù)據(jù)結(jié)構(gòu)。我們都知道 Array 是定長(zhǎng)的,List 是可變長(zhǎng)。而且,List 的實(shí)現(xiàn)類 ArrayList 也是根據(jù) Array 去實(shí)現(xiàn)的。
以下 Array 指代數(shù)組,List 指代數(shù)組列表。
Array 轉(zhuǎn) List
當(dāng)然最原始的方法就是使用遍歷的方式,將 Array 中的元素都添加到 List 中。這種實(shí)現(xiàn)方式這里不作贅述。
Java1.2 之后,Jdk 語(yǔ)言提供 Arrays 這個(gè)工具類。大大簡(jiǎn)化了我們常見(jiàn)的 Array 操作,但是也有不少需要注意的問(wèn)題。
如下:
Integer[] a = { 1, 2 }; List<Integer> l = Arrays.asList(a);這是我們常見(jiàn)的 Array 轉(zhuǎn)換 List 的方式,但是這個(gè)使用上有一個(gè)問(wèn)題。當(dāng)對(duì) List 對(duì)象 l 進(jìn)行列表插入操作時(shí):
l.add(3);程序就會(huì)拋出異常 java.lang.UnsupportedOperationException。這是為什么呢?
查看 Arrays.asList 源碼發(fā)現(xiàn),
public static <T> List<T> asList(T... a) {return new ArrayList<>(a); }這里返回的 ArrayList 并不是 java.util.ArrayList 而是 java.util.Arrays.ArrayList。Arrays 又新建了一個(gè) ArrayList 內(nèi)部類,實(shí)現(xiàn)了一些基本 get set 方法。
回頭查看 java.util.Arrays.ArrayList.ArrayList(E[] array) 構(gòu)造函數(shù),
ArrayList(E[] array) {a = Objects.requireNonNull(array); }不難發(fā)現(xiàn),java.util.Arrays.ArrayList 雖然打著 List 的旗號(hào),繼承了 AbstractList 。但是其只是在 Array 的基礎(chǔ)上進(jìn)行了簡(jiǎn)單的封轉(zhuǎn),AbstractList 中則是直接重寫了 add 方法,表示這個(gè)方法是不允許操作。
public void add(int index, E element) {throw new UnsupportedOperationException(); }明白了這個(gè)錯(cuò)誤產(chǎn)生的原因,回頭想一下 Java 的這些開(kāi)發(fā)者們?yōu)槭裁催@樣設(shè)計(jì)。
ArrayList 中如果要添加一個(gè)元素,則需要先對(duì)其內(nèi)部的 Array 進(jìn)行擴(kuò)容,然后將 Old Array 復(fù)制到擴(kuò)容后的 New Array 中。如果 Array 轉(zhuǎn) List 僅僅是讀取操作,或是在 Array 的 Size 范圍之內(nèi)進(jìn)行替換操作,再將 Array 復(fù)制一遍,不免會(huì)對(duì)內(nèi)存進(jìn)行浪費(fèi),倒不如直接將原始的 Array 直接拿來(lái)維護(hù)更為直接和高效(正如java.util.Arrays.ArrayList的實(shí)現(xiàn)方式)。
明白這個(gè)緣由之后,如果要在 Array 轉(zhuǎn) List 之后,不只有只讀操作,那么則需要下面的實(shí)現(xiàn),
List<Integer> l = new ArrayList<>(Arrays.asList(a));雖然在我們?nèi)粘5拈_(kāi)發(fā)過(guò)程中,已經(jīng)習(xí)慣了使用 ArrayList 去代替 Array,但是了解此處 Java 的轉(zhuǎn)換過(guò)程還是能夠讓我們少踩坑。
List 轉(zhuǎn) Array
因?yàn)?Array 的長(zhǎng)度不可變,所以這個(gè)轉(zhuǎn)換過(guò)程中,會(huì)有長(zhǎng)度不匹配的情況。
常見(jiàn)的轉(zhuǎn)換方法是 ArrayList.toArray() 或 ArrayList.toArray(T[])。
這兩個(gè)實(shí)現(xiàn)的共同點(diǎn)是都是對(duì) ArrayList 中的 Array 進(jìn)行 Copy 操作,生成一個(gè)新的數(shù)組返回。不同點(diǎn)是前者返回值是 Object[],后者是 T[]。
在 ArrayList.toArray(T[]) 的使用過(guò)程中需要注意,當(dāng)要轉(zhuǎn)換的 Array 長(zhǎng)度小于 ArrayList 的 size 時(shí),不要試圖通過(guò)傳入形參的方式進(jìn)行轉(zhuǎn)換,雖然這在 Array 的長(zhǎng)度大于 List 時(shí)不會(huì)出現(xiàn)問(wèn)題。
如下代碼:
// l [1, 2, 3] Integer[] a = new Integer[2]; l.toArray(a); // error 正確寫法:a = l.toArray(a); Stream.of(a).forEach(System.out::println);輸出結(jié)果是:null null。
查看源碼實(shí)現(xiàn):
public <T> 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; }// Arrays.copyOf 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; }可見(jiàn),當(dāng) a.length < size 成立,入?yún)?a 并沒(méi)有被使用,所以 a 依然是 new Integer[2]。
所以,極度建議在轉(zhuǎn)換之前初始化 Array 的長(zhǎng)度為 ArrayList 的 size,并且使用返回值重新給 Array 賦值。
// l [1, 2, 3] Integer[] b = new Integer[l.size()]; b = l.toArray(b); Stream.of(b).forEach(System.out::println);備注
部分內(nèi)容參考:《碼出高效:Java開(kāi)發(fā)手冊(cè)》 一書(shū)。
原文地址
總結(jié)
以上是生活随笔為你收集整理的Array 和 List 的转换问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: swagger导出Word接口文档
- 下一篇: 【C语言】乒乓球比赛问题