The user of this interface has precise control over where in the list each element is inserted. The user can access elements by their integer index (position in the list), and search for elements in the list.
可以看到,List 接口的實現類在實現插入元素時,都會根據索引進行排列。比如 ArrayList,本質是一個數組:LinkedList, 雙向鍊表:由於 List 的元素在存儲時互不幹擾,沒有什麼依賴關係,自然可以重複(這點與 Set 有很大區別)。List 接口定義的方法List 中除了繼承 Collection 的一些方法,還提供以下操作:位置相關:List 和 數組一樣,都是從 0 開始,我們可以根據元素在 list 中的位置進行操作,比如說 get, set, add, addAll, remove;
搜索:從 list 中查找某個對象的位置,比如 indexOf, lastIndexOf;
迭代:使用 Iterator 的拓展版迭代器 ListIterator 進行迭代操作;
範圍性操作:使用 subList 方法對 list 進行任意範圍的操作。
Collection 中 提供的一些方法就不介紹了,不熟悉的可以去看一下。集合的操作remove(Object)
add(E), addAll(Collection<? extends E>)
注意:上述使用了 ArrayList 的轉換構造函數:
public ArrayList(Collection
Object 的 equlas() 方法默認和 == 一樣,比較的是地址是否相等。public boolean equals(Object o) { return this == o;}
因此和 Set,Map 一樣,List 中如果想要根據兩個對象的內容而不是地址比較是否相等時,需要重寫 equals() 和 hashCode() 方法。 remove(),contains(), indexOf() 等等方法都需要依賴它們:
@Override public boolean contains(Object object) { Object[] a = array; int s = size; if (object != null) { for (int i = 0; i < s; i++) { //需要重載 Object 默認的 equals if (object.equals(a[i])) { return true; } } } else { for (int i = 0; i < s; i++) { if (a[i] == null) { return true; } } } return false;}@Override public int indexOf(Object object) { Object[] a = array; int s = size; if (object != null) { for (int i = 0; i < s; i++) { if (object.equals(a[i])) { return i; } } } else { for (int i = 0; i < s; i++) { if (a[i] == null) { return i; } } } return -1;}
兩個 List 對象的所有位置上元素都一樣才能相等。
位置訪問,搜索基礎的位置訪問操作方法有:
get, set, add, remove
indexOf, lastIndexOf
addAll(int,Collection)
public static <E> void swap(List<E> a, int i, int j) { E tmp = a.get(i); a.set(i, a.get(j)); a.set(j, tmp);}
不同的是它是多態的,允許任何 List 的子類使用。 Collections 中的 shuffle 就有用到和下面這種相似的交換方法:
public static void shuffle(List<?> list, Random rnd) { for (int i = list.size(); i > 1; i--) swap(list, i - 1, rnd.nextInt(i));}
這種算法使用指定的隨機算法,從後往前重複的進行交換。和一些其他底層 shuffle 算法不同,這個算法更加公平(隨機方法夠隨機的話,所有元素的被抽到的概率一樣),同時夠快(只要 list.size() -1 )次交換。
局部範圍操作List.subList(int fromIndex, int toIndex) 方法返回 List 在 fromIndex 與 toIndex 範圍內的子集。注意是左閉右開,[fromIndex,toIndex)。
注意! List.subList 方法並沒有像我們想的那樣:創建一個新的 List,然後把舊 List 的指定範圍子元素拷貝進新 List,根!本!不!是!
subList 返回的扔是 List 原來的引用,只不過把開始位置 offset 和 size 改了下,見 List.subList() 在 AbstractList 抽象類中的實現:
public List<E> subList(int start, int end) { if (start >= 0 && end <= size()) { if (start <= end) { if (this instanceof RandomAccess) { return new SubAbstractListRandomAccess<E>(this, start, end); } return new SubAbstractList<E>(this, start, end); } throw new IllegalArgumentException(); } throw new IndexOutOfBoundsException();}
SubAbstractListRandomAccess 最終也是繼承 SubAbstractList,直接看 SubAbstractList:
SubAbstractList(AbstractList<E> list, int start, int end) { fullList = list; modCount = fullList.modCount; offset = start; size = end - start;}
可以看到,的確是保持原來的引用。
所以,重點來了!由於 subList 持有 List 同一個引用,所以對 subList 進行的操作也會影響到原有 List,舉個慄子:
你猜運行結果是什麼?
驗證了上述重點。
所以,我們可以使用 subList 對 List 進行範圍操作,比如下面的代碼,一句話實現了刪除 shixinList 部分元素的操作:shixinList.subList(fromIndex, toIndex).clear();
還可以查找某元素在局部範圍內的位置:
int i = list.subList(fromIndex, toIndex).indexOf(o);int j = list.subList(fromIndex, toIndex).lastIndexOf(o);
List 與 Array 區別?List 在很多方面跟 Array 數組感覺很相似,尤其是 ArrayList,那 List 和數組究竟哪個更好呢?容量固定時優先使用數組,容納類型更多,更高效。在容量不確定的情景下, List 更有優勢,看下 ArrayList 和 LinkedList 如何實現容量動態增長:ArrayList 的擴容機制:public boolean add(E object) { Object[] a = array; int s = size; //當放滿時,擴容 if (s == a.length) { //MIN_CAPACITY_INCREMENT 為常量,12 Object[] newArray = new Object[s + (s < (MIN_CAPACITY_INCREMENT / 2) ? MIN_CAPACITY_INCREMENT : s >> 1)]; System.arraycopy(a, 0, newArray, 0, s); array = a = newArray; } a[s] = object; size = s + 1; modCount++; return true;}
可以看到:LinkedList 的擴容機制:public boolean add(E object) { return addLastImpl(object);}private boolean addLastImpl(E object) { Link<E> oldLast = voidLink.previous; Link<E> newLink = new Link<E>(object, oldLast, voidLink); voidLink.previous = newLink; oldLast.next = newLink; size++; modCount++; return true;}
可以看到,沒!有!擴容機制!這是由於 LinedList 實際上是一個雙向鍊表,不存在元素個數限制,使勁加就行了。transient Link<E> voidLink;private static final class Link<ET> { ET data; Link<ET> previous, next; Link(ET o, Link<ET> p, Link<ET> n) { data = o; previous = p; next = n; }}
List 與 Array 之間的轉換在 List 中有兩個轉換成 數組 的方法:Object[] toArray()
T[] toArray(T[] array)
public Object[] toArray() { int s = size; Object[] result = new Object[s]; //這裡的 array 就是 ArrayList 的底層實現,直接拷貝 //System.arraycopy 是底層方法,效率很高 System.arraycopy(array, 0, result, 0, s); return result;}public <T> T[] toArray(T[] contents) { int s = size; //先判斷參數能不能放下這麼多元素 if (contents.length < s) { //放不下就創建個新數組 @SuppressWarnings("unchecked") T[] newArray = (T[]) Array.newInstance(contents.getClass().getComponentType(), s); contents = newArray; } System.arraycopy(this.array, 0, contents, 0, s); if (contents.length > s) { contents[s] = null; } return contents;}
LinkedList 的實現:public Object[] toArray() { int index = 0; Object[] contents = new Object[size]; Link<E> link = voidLink.next; while (link != voidLink) { //挨個賦值,效率不如 ArrayList contents[index++] = link.data; link = link.next; } return contents;}@Override@SuppressWarnings("unchecked")public <T> T[] toArray(T[] contents) { int index = 0; if (size > contents.length) { Class<?> ct = contents.getClass().getComponentType(); contents = (T[]) Array.newInstance(ct, size); } Link<E> link = voidLink.next; while (link != voidLink) { //還是比 ArrayList 慢 contents[index++] = (T) link.data; link = link.next; } if (index < contents.length) { contents[index] = null; } return contents;}
數組工具類 Arrays 提供了數組轉成 List 的方法 asList :@SafeVarargspublic static <T> List<T> asList(T... array) { return new ArrayList<T>(array);}
使用的是 Arrays 內部創建的 ArrayList 的轉換構造函數:
private final E[] a;ArrayList(E[] storage) { if (storage == null) { throw new NullPointerException("storage == null"); } //直接複製 a = storage;}
迭代器 Iterator, ListIteratorList 繼承了 Collection 的 iterator() 方法,可以獲取 Iterator,使用它可以進行向後遍歷。在此基礎上,List 還可以通過 listIterator(), listIterator(int location) 方法(後者指定了遊標的位置)獲取更強大的迭代器ListIterator。使用 ListIterator 可以對 List 進行向前、向後雙向遍歷,同時還允許進行 add, set, remove 等操作。List 的實現類中許多方法都使用了 ListIterator,比如 List.indexOf() 方法的一種實現:public int indexOf(E e) { for (ListIterator<E> it = listIterator(); it.hasNext(); ) if (e == null ? it.next() == null : e.equals(it.next())) return it.previousIndex(); // Element not found return -1;}
ListIterator 提供了 add, set, remove 操作,他們都是對迭代器剛通過 next(), previous()方法迭代的元素進行操作。下面這個慄子中,List 通過結合 ListIterator 使用,可以實現一個多態的方法,對所有 List 的實現類都適用:public static <E> void replace(List<E> list, E val, E newVal) { for (ListIterator<E> it = list.listIterator(); it.hasNext(); ) if (val == null ? it.next() == null : val.equals(it.next())) it.set(newVal);}
List 的相關算法:集合的工具類 Collections 中包含很多 List 的相關操作算法:sort ,歸併排序
shuffle ,隨機打亂
reverse ,反轉元素順序
swap ,交換
binarySearch ,二分查找
……
具體實現我們後續介紹,感謝關注!關聯: Collection, ListIterator, Collections
Thanks:http://docs.oracle.com/javase/1.5.0/docs/api/java/util/List.html
https://docs.oracle.com/javase/tutorial/collections/interfaces/list.html
http://blog.csdn.net/mazhimazh/article/details/17759579#comments
http://www.blogjava.net/flysky19/articles/92775.html
出處:http://blog.csdn.net/u011240877/article/details/52802849
聲明:版權歸原作者所有。