List是一個有序的Collection(有時稱爲序列),列表可能包含重複元素,除了從Collection
繼承的操做以外,List
接口還包括如下操做:html
get
、set
、add
、addAll
和remove
等方法。indexOf
和lastIndexOf
。Iterator
語義以利用列表的順序性,listIterator
方法提供此行爲。sublist
方法對列表執行任意範圍操做。Java平臺包含兩個通用的List
實現,ArrayList,一般是性能更好的實現,而LinkedList在某些狀況下提供更好的性能。java
假設你已經熟悉它們,那麼從Collection
繼承的操做均可以完成你指望它們作的事情,若是你不熟悉Collection
,如今是閱讀Collection接口部分的好時機,remove
操做始終從列表中刪除指定元素的第一個匹配項,add
和addAll
操做始終將新元素附加到列表的末尾,所以,如下語法將一個列表鏈接到另外一個列表。git
list1.addAll(list2);
這是這個語法的非破壞性形式,它產生第三個List
,其中包含附加到第一個列表的第二個列表。github
List<Type> list3 = new ArrayList<Type>(list1); list3.addAll(list2);
請注意,此語法在其非破壞性形式中利用了ArrayList
的標準轉換構造函數。算法
這是一個將一些名稱聚合到List
中的示例(JDK 8及更高版本):小程序
List<String> list = people.stream() .map(Person::getName) .collect(Collectors.toList());
與Set接口同樣,List
強化了對equals
和hashCode
方法的需求,所以能夠比較兩個List
對象的邏輯相等性,而不考慮它們的實現類,若是兩個List
對象包含相同順序的相同元素,則它們是相等的。segmentfault
基礎的位置訪問操做是get
、set
、add
和remove
(set
和remove
操做返回被覆蓋或刪除的舊值),其餘操做(indexOf
和lastIndexOf
)返回列表中指定元素的第一個或最後一個索引。api
addAll
操做從指定位置開始插入指定Collection
的全部元素,元素按指定Collection
的迭代器返回的順序插入,此調用是Collection
的addAll
操做的位置訪問模擬。數組
這是在List
中交換兩個索引值的一個小方法。併發
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
中的兩個元素,不管其實現類型如何,這是另外一種使用前面swap
方法的多態算法。
public static void shuffle(List<?> list, Random rnd) { for (int i = list.size(); i > 1; i--) swap(list, i - 1, rnd.nextInt(i)); }
此算法包含在Java平臺的Collections類中,使用指定的隨機源隨機置換指定的列表,這有點微妙:它從底部向上運行列表,反覆將隨機選擇的元素交換到當前位置。不像大多數天真的洗牌嘗試,這是公平的(假設一個公平的隨機源,全部排列都有相同的可能性)和快速(須要徹底list.size()-1
交換),如下程序使用此算法以隨機順序打印其參數列表中的單詞。
import java.util.*; public class Shuffle { public static void main(String[] args) { List<String> list = new ArrayList<String>(); for (String a : args) list.add(a); Collections.shuffle(list, new Random()); System.out.println(list); } }
事實上,這個程序能夠更短、更快,Arrays類有一個名爲asList
的靜態工廠方法,它容許將數組視爲List
,此方法不會複製數組,List
中的更改會寫入數組,反之亦然。生成的List
不是通用List
實現,由於它沒有實現(可選)add
和remove
操做:數組不可調整大小。利用Arrays.asList
並調用shuffle
的庫版本(使用默認的隨機源),你將獲得如下微小程序,其行爲與前一個程序相同。
import java.util.*; public class Shuffle { public static void main(String[] args) { List<String> list = Arrays.asList(args); Collections.shuffle(list); System.out.println(list); } }
正如你所指望的那樣,List
的iterator
操做返回的Iterator
以適當的順序返回列表的元素,List
還提供了一個更豐富的迭代器,稱爲ListIterator
,它容許你在任一方向遍歷列表、在迭代期間修改列表、並獲取迭代器的當前位置。
ListIterator
從Iterator
繼承的三個方法(hasNext
、next
和remove
)在兩個接口中徹底相同,hasPrevious
和previous
操做和hasNext
和next
的很類似,前一個操做引用(隱式)遊標以前的元素,然後者引用遊標以後的元素,previous
操做向後移動光標,而next
向前移動光標。
這是在列表中向後迭代的標準語法。
for (ListIterator<Type> it = list.listIterator(list.size()); it.hasPrevious(); ) { Type t = it.previous(); ... }
請注意前面的語法中listIterator
的參數,List
接口有兩種形式的listIterator
方法,不帶參數的形式返回位於列表開頭的ListIterator
,帶有int
參數的形式返回一個位於指定索引處的ListIterator
。索引引用初始調用next
返回的元素,對previous
的初始調用將返回索引爲index-1
的元素,在長度爲n
的列表中,index
有n+1
個有效值,從0
到n
(包括n
)。
直觀地說,遊標老是在兩個元素之間 — 一個將經過調用previous
返回,一個將經過調用next
返回。n+1
個有效索引值對應於元素之間的n+1
個間隙,從第一個元素以前的間隙到最後一個元素以後的間隙,下圖顯示了包含四個元素的列表中的五個可能的遊標位置。
對next
和previous
的調用能夠混合使用,可是必須當心一點,對previous
的第一次調用返回與對next
的最後一次調用相同的元素,相似地,在對previous
進行一系列調用以後,對next
的第一次調用與對previous
的最後一次調用返回相同的元素。
nextIndex
方法返回後續調用next
返回的元素的索引,而且previousIndex
返回後續調用previous
返回的元素的索引,這些調用一般用於報告找到某些內容的位置或記錄ListIterator
的位置,以即可以建立具備相同位置的另外一個ListIterator
。
一樣也不足爲奇的是,nextIndex
返回的數字老是大於previousIndex
返回的數字,這意味着兩種邊界狀況的行爲:(1)當光標位於初始元素以前時對previousIndex
的調用返回-1
,當光標位於最後一個元素以後時調用nextIndex
返回list.size()
。爲了使全部這些具體化,如下是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; }
請注意,indexOf
方法返回it.previousIndex()
,即便它正在向前遍歷列表,緣由是it.nextIndex()
將返回咱們要檢查的元素的索引,而且咱們想要返回剛檢查的元素的索引。
Iterator
接口提供remove
操做以從Collection
中刪除next
返回的最後一個元素,對於ListIterator
,此操做將刪除next
或previous
返回的最後一個元素。ListIterator
接口提供了兩個額外的操做來修改列表 — set
和add
,set
方法用指定的元素覆蓋next
或previous
返回的最後一個元素,如下多態算法使用set
將一個指定值的全部出現替換爲另外一個。
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); }
在這個例子中惟一的棘手是val
和it.next
之間的相等性測試,你須要特殊狀況下val
值爲null
以防止NullPointerException
。
add
方法在當前光標位置以前當即將新元素插入到列表中,此方法在如下多態算法中說明,以使用指定列表中包含的值序列替換指定值的全部出現。
public static <E> void replace(List<E> list, E val, List<? extends E> newVals) { for (ListIterator<E> it = list.listIterator(); it.hasNext(); ){ if (val == null ? it.next() == null : val.equals(it.next())) { it.remove(); for (E e : newVals) it.add(e); } } }
範圍視圖操做subList(int fromIndex,int toIndex)
返回此列表部分的List
視圖,其索引範圍從fromIndex
(包括)到toIndex
(不包括),這個半開放範圍反映了典型的for循環。
for (int i = fromIndex; i < toIndex; i++) { ... }
正如術語視圖所暗示的那樣,返回的List
由調用了subList
的List
進行備份,所以前者中的更改將反映在後者中。
此方法消除了對顯式範圍操做的須要(對於數組一般存在的排序),任何指望List
的操做均可以經過傳遞subList
視圖而不是整個List
來用做範圍操做,例如,如下語句從List
中刪除了一系列元素。
list.subList(fromIndex, toIndex).clear();
能夠構造相似的語句以搜索範圍中的元素。
int i = list.subList(fromIndex, toIndex).indexOf(o); int j = list.subList(fromIndex, toIndex).lastIndexOf(o);
請注意,前面的語句返回subList
中找到的元素的索引,而不是支持列表中的索引。
在List
上操做的任何多態算法(例如replace
和shuffle
示例)都與subList
返回的List
一塊兒使用。
這是一個多態算法,其實現使用subList
來處理來自牌組的牌,也就是說,它返回一個新的List
(「hand」),它包含從指定List
(「deck」)末尾獲取的指定數量的元素,手中返回的元素將從牌組中移除。
public static <E> List<E> dealHand(List<E> deck, int n) { int deckSize = deck.size(); List<E> handView = deck.subList(deckSize - n, deckSize); List<E> hand = new ArrayList<E>(handView); handView.clear(); return hand; }
請注意,此算法將牌從牌組末端移除,對於許多常見的List
實現,例如ArrayList
,從列表末尾刪除元素的性能明顯優於從頭開始刪除元素的性能。
如下是一個程序,它將dealHand
方法與Collections.shuffle
結合使用,從正常的52張卡牌中生成牌局,該程序採用兩個命令行參數:(1)手牌數(2)每手牌數。
import java.util.*; public class Deal { public static void main(String[] args) { if (args.length < 2) { System.out.println("Usage: Deal hands cards"); return; } int numHands = Integer.parseInt(args[0]); int cardsPerHand = Integer.parseInt(args[1]); // Make a normal 52-card deck. String[] suit = new String[] { "spades", "hearts", "diamonds", "clubs" }; String[] rank = new String[] { "ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "jack", "queen", "king" }; List<String> deck = new ArrayList<String>(); for (int i = 0; i < suit.length; i++) for (int j = 0; j < rank.length; j++) deck.add(rank[j] + " of " + suit[i]); // Shuffle the deck. Collections.shuffle(deck); if (numHands * cardsPerHand > deck.size()) { System.out.println("Not enough cards."); return; } for (int i = 0; i < numHands; i++) System.out.println(dealHand(deck, cardsPerHand)); } public static <E> List<E> dealHand(List<E> deck, int n) { int deckSize = deck.size(); List<E> handView = deck.subList(deckSize - n, deckSize); List<E> hand = new ArrayList<E>(handView); handView.clear(); return hand; } }
運行程序會產生以下輸出。
% java Deal 4 5 [8 of hearts, jack of spades, 3 of spades, 4 of spades, king of diamonds] [4 of diamonds, ace of clubs, 6 of clubs, jack of hearts, queen of hearts] [7 of spades, 5 of spades, 2 of diamonds, queen of diamonds, 9 of clubs] [8 of spades, 6 of diamonds, ace of spades, 3 of hearts, ace of hearts]
儘管subList
操做很是強大,但在使用它時必須當心,若是以經過返回的List
以外的任何方式向支持List
添加或刪除元素,則subList
返回的List
的語義將變爲undefined
。所以,強烈建議你僅將subList
返回的List
用做臨時對象 — 在支持List
上執行一個或一系列範圍操做,使用subList
實例的時間越長,經過直接修改支持List
或經過另外一個subList
對象來破壞它的可能性就越大,請注意,修改子列表的子列表並繼續使用原始子列表(儘管不是併發)是合法的。
Collections
類中的大多數多態算法專門應用於List
,擁有全部這些算法能夠很容易地操做列表,如下是這些算法的摘要,這些算法在「算法」部分中有更詳細的描述。
sort
— 使用合併排序算法對List
進行排序,該算法提供快速、穩定的排序(穩定排序是不從新排序相同元素的排序)。shuffle
— 隨機置換List
中的元素。reverse
— 反轉List
中元素的順序。rotate
— 將List
中的全部元素旋轉指定的距離。swap
— 交換列表中指定位置的元素。replaceAll
— 將全部出現的一個指定值替換爲另外一個。fill
— 用指定的值覆蓋List
中的每一個元素。copy
— 將源列表複製到目標列表。binarySearch
— 使用二進制搜索算法搜索有序List
中的元素。indexOfSubList
— 返回一個List
的第一個子列表的索引,該列表等於另外一個。lastIndexOfSubList
— 返回一個List
的最後一個子列表的索引,該列表等於另外一個。