List接口
List是一個有序的Collection(有時稱為序列),列表可能包含重復元素,除了從Collection繼承的操作之外,List接口還包括以下操作:
位置訪問 — 根據列表中的數字位置操縱元素,這包括get、set、add、addAll和remove等方法。
搜索 — 搜索列表中的指定對象并返回其數字位置,搜索方法包括indexOf和lastIndexOf。
迭代 — 擴展Iterator語義以利用列表的順序性,listIterator方法提供此行為。
范圍視圖 — sublist方法對列表執行任意范圍操作。
Java平臺包含兩個通用的List實現,ArrayList,通常是性能更好的實現,而LinkedList在某些情況下提供更好的性能。
集合操作假設你已經熟悉它們,那么從Collection繼承的操作都可以完成你期望它們做的事情,如果你不熟悉Collection,現在是閱讀Collection接口部分的好時機,remove操作始終從列表中刪除指定元素的第一個匹配項,add和addAll操作始終將新元素附加到列表的末尾,因此,以下語法將一個列表連接到另一個列表。
list1.addAll(list2);
這是這個語法的非破壞性形式,它產生第三個List,其中包含附加到第一個列表的第二個列表。
Listlist3 = new ArrayList (list1); list3.addAll(list2);
請注意,此語法在其非破壞性形式中利用了ArrayList的標準轉換構造函數。
這是一個將一些名稱聚合到List中的示例(JDK 8及更高版本):
Listlist = people.stream() .map(Person::getName) .collect(Collectors.toList());
與Set接口一樣,List強化了對equals和hashCode方法的需求,因此可以比較兩個List對象的邏輯相等性,而不考慮它們的實現類,如果兩個List對象包含相同順序的相同元素,則它們是相等的。
位置訪問和搜索操作基礎的位置訪問操作是get、set、add和remove(set和remove操作返回被覆蓋或刪除的舊值),其他操作(indexOf和lastIndexOf)返回列表中指定元素的第一個或最后一個索引。
addAll操作從指定位置開始插入指定Collection的所有元素,元素按指定Collection的迭代器返回的順序插入,此調用是Collection的addAll操作的位置訪問模擬。
這是在List中交換兩個索引值的一個小方法。
public staticvoid swap(List 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) { Listlist = new ArrayList (); 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迭代器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 (ListIteratorit = 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 (ListIteratorit = 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 staticvoid replace(List list, E val, E newVal) { for (ListIterator 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范圍視圖操作void replace(List list, E val, List extends E> newVals) { for (ListIterator 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 staticList dealHand(List deck, int n) { int deckSize = deck.size(); List handView = deck.subList(deckSize - n, deckSize); List hand = new ArrayList (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" }; Listdeck = new ArrayList (); 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 List dealHand(List deck, int n) { int deckSize = deck.size(); List handView = deck.subList(deckSize - n, deckSize); List hand = new ArrayList (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對象來破壞它的可能性就越大,請注意,修改子列表的子列表并繼續使用原始子列表(盡管不是并發)是合法的。
List算法Collections類中的大多數多態算法專門應用于List,擁有所有這些算法可以很容易地操作列表,以下是這些算法的摘要,這些算法在“算法”部分中有更詳細的描述。
sort — 使用合并排序算法對List進行排序,該算法提供快速、穩定的排序(穩定排序是不重新排序相同元素的排序)。
shuffle — 隨機置換List中的元素。
reverse — 反轉List中元素的順序。
rotate — 將List中的所有元素旋轉指定的距離。
swap — 交換列表中指定位置的元素。
replaceAll — 將所有出現的一個指定值替換為另一個。
fill — 用指定的值覆蓋List中的每個元素。
copy — 將源列表復制到目標列表。
binarySearch — 使用二進制搜索算法搜索有序List中的元素。
indexOfSubList — 返回一個List的第一個子列表的索引,該列表等于另一個。
lastIndexOfSubList — 返回一個List的最后一個子列表的索引,該列表等于另一個。
上一篇:Set接口 下一篇:Queue接口文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/73219.html
集合接口 核心集合接口封裝了不同類型的集合,如下圖所示,這些接口允許獨立于其表示的細節來操縱集合,核心集合接口是Java集合框架的基礎,如下圖所示,核心集合接口形成層次結構。 showImg(https://segmentfault.com/img/bVbntJW?w=402&h=146); Set是一種特殊的Collection,SortedSet是一種特殊的Set,依此類推,另請注意,層次結構...
Queue接口 Queue是在處理之前保存元素的集合,除了基本的Collection操作外,隊列還提供額外的插入、刪除和檢查操作,Queue接口如下。 public interface Queue extends Collection { E element(); boolean offer(E e); E peek(); E poll(); E remov...
默認方法 接口部分描述了一個涉及計算機控制汽車制造商的例子,他們發布了行業標準接口,描述了可以調用哪些方法來操作他們的汽車,如果那些計算機控制的汽車制造商為他們的汽車添加新的功能,如飛行,該怎么辦?這些制造商需要指定新的方法,以使其他公司(如電子制導儀器制造商)能夠使其軟件適應飛行汽車,這些汽車制造商將在哪里聲明這些與飛行有關的新方法?如果他們將它們添加到原始接口,那么實現了這些接口的程序員將不得...
泛型、繼承和子類型 如你所知,只要類型兼容,就可以將一種類型的對象分配給另一種類型的對象,例如,你可以將Integer分配給Object,因為Object是Integer的超類型之一: Object someObject = new Object(); Integer someInteger = new Integer(10); someObject = someInteger; // OK ...
泛型 在任何重要的軟件項目中,bug都是不可避免的,仔細的規劃、編程和測試可以幫助減少它們的普遍性,但是它們總會在某個地方以某種方式潛入你的代碼,隨著新功能的引入以及你的代碼庫在規模和復雜性方面的增長,這一點變得尤為明顯。 幸運的是,一些錯誤比其他錯誤更容易被發現,例如,編譯時錯誤可以在早期檢測到,你可以使用編譯器的錯誤消息來確定問題所在并立即修復它。但是,運行時錯誤可能會更成問題,它們并不總是立...
閱讀 3022·2021-11-16 11:42
閱讀 3675·2021-09-08 09:36
閱讀 956·2019-08-30 12:52
閱讀 2490·2019-08-29 14:12
閱讀 780·2019-08-29 13:53
閱讀 3592·2019-08-29 12:16
閱讀 649·2019-08-29 12:12
閱讀 2478·2019-08-29 11:16