java原始碼分析 Arrays.asList()與Collections.unmodifiableList()
舉個栗子
本章示例程式碼來自java程式設計思想——17.4.1未獲支援的操作——Unsupported類。
import java.util.*;
public class Unsupported {
static void test(String msg, List<String> list) {
System.out.println("--- " + msg + " ---");
Collection<String> c = list;
Collection<String> subList = list.subList(1,8);
// Copy of the sublist:
Collection<String> c2 = new ArrayList<String>(subList);
try { c.retainAll(c2); } catch(Exception e) {
System.out.println("retainAll(): " + e);
}
try { c.removeAll(c2); } catch(Exception e) {
System.out.println("removeAll(): " + e);
}
try { c.clear(); } catch(Exception e) {
System.out.println("clear(): " + e);
}
try { c.add("X"); } catch(Exception e) {
System.out.println("add(): " + e);
}
try { c.addAll(c2); } catch(Exception e) {
System.out.println("addAll(): " + e);
}
try { c.remove("C"); } catch(Exception e) {
System.out.println("remove(): " + e);
}
// The List.set() method modifies the value but
// doesn't change the size of the data structure:
try {
list.set(0, "X");
} catch(Exception e) {
System.out.println("List.set(): " + e);
}
}
public static void main(String[] args) {
List<String> list =
Arrays.asList("A B C D E F G H I J K L".split(" "));
test("Modifiable Copy", new ArrayList<String>(list));
test("Arrays.asList()", list);
test("unmodifiableList()",
Collections.unmodifiableList(
new ArrayList<String>(list)));
}
} /* Output:
--- Modifiable Copy ---
--- Arrays.asList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
--- unmodifiableList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
List.set(): java.lang.UnsupportedOperationException
*///:~
在主函式內先用Arrays這個工具類的asList方法生成了一個List
。在呼叫靜態函式test方法時,先後測試了3種List:
ArrayList
的構造器,構造器簽名是:public ArrayList(Collection<? extends E> c)
。Arrays
工具類的靜態方法asList
方法,其方法簽名是public static <T> List<T> asList(T... a)
。Collections
工具類的靜態方法unmodifiableList
方法,其方法簽名是public static <T> List<T> unmodifiableList(List<? extends T> list)
。
這3種List的定義分別在:
ArrayList
Arrays$ArrayList
(注意是Arrays的靜態內部類)Collections$UnmodifiableRandomAccessList
(注意是Collections的靜態內部類)
在靜態方法test中,有一句Collection<String> subList = list.subList(1,8)
,這個list是傳入的形參,其型別是List<String>
。而List<E>
介面中的subList
方法是沒有預設實現的,這意味著上面測試的3種List都是把subList
方法進行了各自的實現了的。實現的位置分別在:
ArrayList
裡。- 本應該在
Arrays$ArrayList
裡,但由於沒有override,所以實現在Arrays$ArrayList
的父類AbstractList
裡。 Collections$UnmodifiableList
裡或者Collections$UnmodifiableRandomAccessList
裡(都是Collections的靜態內部類),由於主函式傳參是new ArrayList<String>(list))
(ArrayList繼承了標記介面RandomAccess),所以靜態方法unmodifiableList
方法返回的是Collections$UnmodifiableRandomAccessList
的例項。
關於subList的各自實現本文不會深究,讀者只需要知道List<E>
介面中的subList
方法簽名是:List<E> subList(int fromIndex, int toIndex)
即可。
靜態方法test裡,就是去測試型別為List<String>
的傳入形參list,看它是否對於List<E>
介面裡的各個可選操作是否都進行了實現。當List<E>
的實現類不支援這些可選操作時,其實現會是直接丟擲UnsupportedOperationException異常。
從列印結果來看:
ArrayList
支援所有的可選操作(因為一個UnsupportedOperationException異常都沒有捕獲到),所以要想擁有完備的功能,還是得使用ArrayList
啊。Arrays$ArrayList
除了set()
操作外,都不支援。Collections$UnmodifiableRandomAccessList
都不支援,名副其實的Unmodifiable不可修改。
Arrays.asList()原始碼
public class Arrays {
/*省略*/
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);//呼叫靜態內部類的構造器
}
/**
* @serial include
*/
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable//看起來和真正的ArrayList好像差不多
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;//內部實現只是個陣列
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
@Override
public int size() {
return a.length;
}
@Override
public Object[] toArray() {
return a.clone();
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {//不是可選操作哦,成員的泛型方法
int size = size();
if (a.length < size)//如果傳入陣列大小<成員陣列大小,那麼直接忽略傳入陣列,返回一個新的陣列
return Arrays.copyOf(this.a, size,//新的陣列大小和成員陣列大小一樣
(Class<? extends T[]>) a.getClass());
System.arraycopy(this.a, 0, a, 0, size);//如果傳入陣列大小>=成員陣列大小,執行這句
if (a.length > size)//如果傳入陣列大小>成員陣列大小
a[size] = null;
return a;
}
@Override
public E get(int index) {
return a[index];//依靠陣列的取下標操作
}
@Override
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
@Override
public int indexOf(Object o) {
E[] a = this.a;
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else {
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(a, Spliterator.ORDERED);
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (E e : a) {
action.accept(e);
}
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
E[] a = this.a;
for (int i = 0; i < a.length; i++) {
a[i] = operator.apply(a[i]);
}
}
@Override
public void sort(Comparator<? super E> c) {
Arrays.sort(a, c);
}
}
/*省略*/
}
Arrays.asList返回了一個fixed-size固定大小的List,它繼承AbstractList
時除了實現了必要的get() & size()
外,還實現了set()
方法,畢竟set()
方法不會違背fixed-size這個概念。
但是這個靜態內部類沒有去overrideadd() & remove()
,這意味著它將使用繼承於AbstractList
的實現——直接丟擲UnsupportedOperationException異常。所以在使用Arrays.asList
返回的List
時一定要注意到,add() & remove()
方法我們是不能使用的。
在toArray方法裡面,可能會呼叫到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;
}
三目表示式裡,先判斷(Object)newType == (Object)Object[].class)
的返回值,這裡是想判斷一下傳入的Class
物件的型別標籤是否為Object[]
,如果是那就好辦了,直接建立Object的陣列——new Object[newLength]
。但其型別標籤如果不是Object[]
,又想建立新陣列,那就必須用到Array.newInstance
了(注意是反射包裡的java.lang.reflect.Array
)。
之所以一定要用三目表示式而不直接使用Array.newInstance
,我個人猜想是:使用反射來建立陣列必定有著效率的問題,而如果型別標籤就是Object[]
,那麼就沒有必要使用反射了,直接建立Object陣列就可以更快執行。
注意在給Arrays.asList傳參時,不能將基本型別陣列作為引數。
import java.util.Arrays;
import java.util.List;
public class test1 {
public static void main(String[] args) {
int[] myArray = { 1, 2, 3 };
List myList = Arrays.asList(myArray);
System.out.println(myList.size());
}
}
程式碼列印結果居然是1,而debug也看出來,myList的元素型別居然是int[3]
。之所以列印結果不是3、且myList的元素型別不是int
,是因為asList 方法簽名:
public static <T> List<T> asList(T... a)
可見其形參型別是T的可變引數,雖然java的自動拆裝箱配合泛型方法的型別推斷,能在單個基本型別時起到作用,但是遇到了基本型別的陣列時,自動拆裝箱就會失效了,所以List myList = Arrays.asList(myArray)
這裡,程式認為你只傳入了一個引數,且這個引數的型別是int[]
。
相對的,在單個基本型別時,java的自動拆裝箱就可以配合泛型方法的型別推斷一起搞事情了,比如List myList = Arrays.asList(4)
這句,debug效果如下:
Collections.unmodifiableList()原始碼
public static <T> List<T> unmodifiableList(List<? extends T> list) {
return (list instanceof RandomAccess ?
new UnmodifiableRandomAccessList<>(list) :
new UnmodifiableList<>(list));
}
unmodifiableList方法先判斷形參list是否屬於RandomAccess的,RandomAccess是一個標記介面,代表可以隨機存取,比如一般List就可以隨機訪問(其實就是其內部實現是陣列,陣列取下標操作當然就是隨機訪問了),而Set就不可以隨機訪問。根據是否屬於RandomAccess,返回相應的例項。
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
implements List<E> {
private static final long serialVersionUID = -283967356065247728L;
final List<? extends E> list;
UnmodifiableList(List<? extends E> list) {
super(list);
this.list = list;
}
public boolean equals(Object o) {return o == this || list.equals(o);}
public int hashCode() {return list.hashCode();}
public E get(int index) {return list.get(index);}//非可選操作裡,就實現了get
public E set(int index, E element) {//其他的非可選操作,都是拋異常
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
public int indexOf(Object o) {return list.indexOf(o);}
public int lastIndexOf(Object o) {return list.lastIndexOf(o);}
public boolean addAll(int index, Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
throw new UnsupportedOperationException();
}
@Override
public void sort(Comparator<? super E> c) {
throw new UnsupportedOperationException();
}
public ListIterator<E> listIterator() {return listIterator(0);}
public ListIterator<E> listIterator(final int index) {
return new ListIterator<E>() {
private final ListIterator<? extends E> i
= list.listIterator(index);
public boolean hasNext() {return i.hasNext();}
public E next() {return i.next();}
public boolean hasPrevious() {return i.hasPrevious();}
public E previous() {return i.previous();}
public int nextIndex() {return i.nextIndex();}
public int previousIndex() {return i.previousIndex();}
public void remove() {//當然,ListIterator也直接拋異常,如果改成呼叫i成員的remove,可能不會拋異常的
throw new UnsupportedOperationException();
}
public void set(E e) {
throw new UnsupportedOperationException();
}
public void add(E e) {
throw new UnsupportedOperationException();
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
i.forEachRemaining(action);
}
};
}
public List<E> subList(int fromIndex, int toIndex) {
return new UnmodifiableList<>(list.subList(fromIndex, toIndex));
}
/**
* UnmodifiableRandomAccessList instances are serialized as
* UnmodifiableList instances to allow them to be deserialized
* in pre-1.4 JREs (which do not have UnmodifiableRandomAccessList).
* This method inverts the transformation. As a beneficial
* side-effect, it also grafts the RandomAccess marker onto
* UnmodifiableList instances that were serialized in pre-1.4 JREs.
*
* Note: Unfortunately, UnmodifiableRandomAccessList instances
* serialized in 1.4.1 and deserialized in 1.4 will become
* UnmodifiableList instances, as this method was missing in 1.4.
*/
private Object readResolve() {
return (list instanceof RandomAccess
? new UnmodifiableRandomAccessList<>(list)
: this);
}
}
/**
* @serial include
*/
static class UnmodifiableRandomAccessList<E> extends UnmodifiableList<E>
implements RandomAccess
{
UnmodifiableRandomAccessList(List<? extends E> list) {
super(list);
}
public List<E> subList(int fromIndex, int toIndex) {
return new UnmodifiableRandomAccessList<>(
list.subList(fromIndex, toIndex));
}
private static final long serialVersionUID = -2542308836966382001L;
/**
* Allows instances to be deserialized in pre-1.4 JREs (which do
* not have UnmodifiableRandomAccessList). UnmodifiableList has
* a readResolve method that inverts this transformation upon
* deserialization.
*/
private Object writeReplace() {
return new UnmodifiableList<>(list);
}
}
UnmodifiableList的實現其實就是套一個殼子而已,所以操作都依賴於構造器裡傳入的那個List。
UnmodifiableList是名副其實的不可修改,那些常用操作裡面,它就只實現了get()
方法,連set()
都不讓用。注意Collections$unmodifiableList
還繼承了Collections$UnmodifiableCollection
,後者已經幫忙實現了size() isEmpty() contains() iterator()
等方法,當然它的實現風格也是讓很多方法拋UnsupportedOperationException異常。
UnmodifiableRandomAccessList的實現就簡單,直接繼承UnmodifiableList就好了。注意這二者的區別就是:UnmodifiableRandomAccessList
代表了可隨機訪問,UnmodifiableList
代表了不可以隨機訪問。
注意到,UnmodifiableRandomAccessList有一個writeReplace
方法必然返回一個非隨機訪問的不可修改列表;而UnmodifiableList有一個readResolve
方法可能返回一個隨機訪問的不可修改列表。從這兩個方法的邏輯可以理解出:隨機訪問的列表可以直接轉換為非隨機訪問的列表(writeReplace
方法),但非隨機訪問的列表要想轉換為隨機訪問的列表的前提是,此列表原本就是可隨機訪問的(readResolve
方法)。這種關係就類似於父類物件和子類物件之間的互相轉換。
相關文章
- Java 容器原始碼分析之 Deque 與 ArrayDequeJava原始碼
- 11款Java工具:原始碼優化與分析Java原始碼優化
- java 原始碼分析 —BooleanJava原始碼Boolean
- Java 原始碼如何分析?Java原始碼
- Java:HashMap原始碼分析JavaHashMap原始碼
- Java Collections 原始碼分析Java原始碼
- Java容器原始碼學習--ArrayList原始碼分析Java原始碼
- Java String原始碼分析Java原始碼
- 【Java】ServiceLoader原始碼分析Java原始碼
- 【Java集合】ArrayList原始碼分析Java原始碼
- PowerUsageSummary.java原始碼分析Java原始碼
- BatteryStatsHelper.java原始碼分析BATJava原始碼
- JAVA集合:ArrayList原始碼分析Java原始碼
- Java 集合包原始碼分析Java原始碼
- Java LinkedList 原始碼分析Java原始碼
- Java原始碼分析:Guava之不可變集合ImmutableMap的原始碼分析Java原始碼Guava
- Collections.unmodifiableList方法的使用與場景
- Java集合原始碼分析(十四):TreeMapJava原始碼
- java基礎:ArrayList — 原始碼分析Java原始碼
- java基礎:HashMap — 原始碼分析JavaHashMap原始碼
- java基礎:Enum — 原始碼分析Java原始碼
- java基礎:Integer — 原始碼分析Java原始碼
- java基礎:TreeMap — 原始碼分析Java原始碼
- Java集合原始碼分析(九)——HashSetJava原始碼
- java集合原始碼分析(六):HashMapJava原始碼HashMap
- Java 集合框架------ArrayList原始碼分析Java框架原始碼
- java集合原始碼分析(三):ArrayListJava原始碼
- Java基礎——HashMap原始碼分析JavaHashMap原始碼
- 分析 java.util.Hashtable 原始碼Java原始碼
- Java-- String原始碼分析Java原始碼
- Java HashMap例項原始碼分析JavaHashMap原始碼
- LeakCanary詳解與原始碼分析原始碼
- MapReduce 詳解與原始碼分析原始碼
- Sentinel基本使用與原始碼分析原始碼
- LinkedList與Queue原始碼分析原始碼
- Java容器類框架分析(5)HashSet原始碼分析Java框架原始碼
- Java容器類框架分析(1)ArrayList原始碼分析Java框架原始碼
- Java 8 ArrayList 原始碼簡單分析Java原始碼