Java中的List集合
集合概述
為了在程式中儲存數目不確定的物件,JDK中提供了一系列的特殊類,這些類可以儲存任意型別的物件,並且長度可變,在Java中這些類被統稱為集合。集合類都位於java.util包中。
集合按照其儲存型別分為兩大類,即單列集合Collection和雙列集合Map,這兩種集合的特點如下:
Collction:單列集合類的根介面,用於儲存一系列符合某種規則的元素,它由兩個重要的子介面,分別是List和Set。其中List的特點是元素有序、可重複。Set的特點是元素無序,而且不可重複。List介面的朱啊喲實現類有ArrayList和LinkedList。Set介面的主要實現類有HashSet和TreeSet
Map:
Collection介面
Collection是所有單列集合的父介面,因此在Collection中定義了單列集合(List和Set)通用的一些方法,這些方法可用於操作所有的單列集合(查詢API文件)
1. add(E e) 將指定物件儲存到容器中 2. addAll() 將指定集合中的元素新增到呼叫該方法的集合中 3. size() 返回列表中的元素數。 4. contains(Object o) 判斷集合中是否包含引數指定的單個元素 5. containsAll(Collection<?> c) 表示判斷集合中是否包含指定元素的整體 6. remove(int index) 移除列表中指定位置的元素 7. removeAll(Collection<?> c) 從列表中移除指定 collection 中包含的其所有元素 8. clear() 從列表中移除所有元素(可選操作)。 9. isEmpty() 如果列表不包含元素,則返回 true。 10. set(int index, E element) 將集合中指定位置元素進行替換 11. toArray() 將集合轉換成陣列 12. retainAll(Collection<?> c) 計算兩個集合的交集
List介面
List介面繼承自Collection介面,是單列集合的一個重要分支,習慣性的實現了List介面的物件稱為List集合。在List集合中允許出現**重複**的元素,所有的元素是以一種線性方式儲存的,在程式中可以通過索引來訪問集合中的指定元素,另外,List集合還有一個特點就是元素**有序**,即元素的存入順序和取出順序一致。
List作為Collection集合的子介面,不但繼承了Collection介面中的全部方法,而且還增加了一些根據元素索引來操作集合的特有方法(查詢API文件),所有的List實現類都可以通過呼叫這些方法來對集合元素進行操作。
add(E e) - 向列表末尾追加元素 add(int index,E e) - 在指定位置上新增一個物件 addAll(Collection<? extends E> c) - 將集合元素新增到指定集合的末尾 get(int index) 返回指定位置的物件 remove(int index) 刪除指定位置的物件 set(int index, E element) - 用指定元素替換列表中指定位置的元素(可選操作)。 indexOf(Object o) - 返回第一個匹配物件的位置 lastIndexOf(Object o) - 返回最後一個匹配物件的索引 size() - 返回此列表中的元素數。
ArrayList集合
ArrayList是List介面的一個實現類,它是程式中最常見的一種集合。在ArrayList內部封裝了一個長度可變的陣列物件,當存入的元素超過陣列長度時,ArrayList會在記憶體中分配一個更大的陣列來儲存這些元素,因此可以將ArrayList集合看作一個長度可變的陣列。
ArrayList集合中大部分方法都是從父類Collection和List繼承過來的,其中,add()方法和get方法用於實現元素的存取。接下來通過一個案例來學習ArrayList集合如何存取元素。
public class Example01 { public static void main(String[] args) { ArrayList list = new ArrayList(); //建立ArrayList集合 list.add("老大"); //向集合中新增元素 list.add("老二"); list.add("老三"); list.add("老四"); System.out.println("集合的長度是: "+list.size()); System.out.println("第二個元素是: "+list.get(1)); } }
由於ArrayList 集合的底層是使用一個數組來儲存元素的,在增加或刪除指定位置的元素時,會導致建立新的陣列,效率比較低,因此不適合做大量的增刪操作。但這種陣列的結構允許程式通過索引的方式來訪問陣列,因此使用ArrayList集合查詢元素很便捷。
LinkedList集合
由於ArrayList 集合在查詢元素時速度很快,但在增刪元素時效率較低。為了克服這種侷限性,可以使用List介面的另一個實現類LinkedList。該集合內部維護了一個雙向迴圈連結串列,連結串列中的每一個元素都使用引用的方式來記住它的前一個元素和後一個元素,從而可以將所有的元素彼此連線起來。當插入一個新元素時,只需要修改元素之間的這種引用關係即可,刪除一個節點也是如此。正因為這樣的儲存結構,LinkedList集合對於元素的增刪操作具有很高的效率。
void add(index,E element) 在此列表中指定的位置插入指定的元素 void addFirst(Object o) 將指定元素插入次列表的開頭 void addLastt(Object o) 將指定元素插入次列表的結尾 Object getFirst() 返回此列表中的第一個元素 Object getLast() 返回此列表中的最後一個元素 Object Remove(int idnex) 移除指定下標的元素 Object removeFirst() 移除並返回次列表中的第一個元素 Object removeFirst() 移除並返回次列表中的最後一個元素
針對集合中的元素進行增加、刪除和獲取操作示例:
public class Example02 { public static void main(String[] args) { LinkedList link = new LinkedList(); link.add("老大"); link.add("老二"); link.add("老三"); link.add("老四"); System.out.println(link.toString()); //取出並列印該集合中的元素 link.add(3, "Student"); //向該集合中指定位置插入資料 link.addFirst("First"); //向該集合第一個位置插入元素 System.out.println(link); System.out.println(link.getFirst()); //取出該集合中第一個元素 link.remove(3); link.removeFirst(); System.out.println(link); } }
ArrayList和LinkedList區別:
ArrayList
陣列實現,查詢快、增刪慢
由於是陣列實現,在增加和刪除的時候會牽扯陣列擴容以及拷貝元素,所以慢。陣列是可以直接按索引查詢,所以在查詢的時候較為快。
LinkedList
連結串列實現,增刪塊、查詢慢
由於連結串列實現,增加時候只要讓前一個元素記住自己就可以,刪除時候讓前一個元素記住後一個元素,後一個元素記錄前一個元素這樣的增刪效率比較高,但查詢時需要一個一個的遍歷,所以效率較低。
Iterator介面
Iterator主要用於迭代訪問(即遍歷)Collection中的元素,因此Iterator物件也被稱為迭代器
迭代器遍歷步驟:
1. 呼叫Iterator()得到一個指向集合序列第一個元素的迭代器
2. 用迴圈呼叫hasNext()方法,如果有元素,返回true
3. 在迴圈中,使用next()方法獲取集合中的下一個元素
學習Iterator迭代集合中的元素
public class Example03 { public static void main(String[] args) { ArrayList list = new ArrayList(); //建立ArrayList集合 list.add("老大"); //向集合中新增字串 list.add("老二"); list.add("老三"); list.add("老四"); Iterator it = list.iterator(); //獲取集合的Iterator物件 while(it.hasNext()) { //判斷ArrayList中是否存在下一個元素 Object obj = it.next(); //取出ArrayList集合中的元素 System.out.println(obj); } } }
需要特別說明的是,當通過迭代器獲取ArrayList 集合中的元素時,都會將這些元素當做Object型別來看待,如果想得到特定型別的元素,則需要進行強制轉換
JDK5.0新特性——foreach迴圈
foreach迴圈時一種更加簡潔的for迴圈,也稱增強for迴圈。foreach迴圈用於遍歷陣列或集合中的元素,其具體語法格式如下:
for(容器中元素型別 臨時變數:容器變數){ 執行語句 }
與for迴圈相比,foreach迴圈不需要獲取容器的長度,也不需要根據索引訪問容器中的元素,但它會自動遍歷容器中的每個元素。下面通過一個案例對foreach迴圈進行詳細講解
public class Example04 { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("小紅"); list.add("小明"); list.add("小綠"); for (Object object : list) { System.out.println(object); } } }
腳下留心:
1. foreach迴圈雖然書寫起來很簡潔,但是使用時也存在一定的侷限性,當使用foreach迴圈遍歷集合和陣列時,只能訪問集合中的元素,不能對其中的元素進行修改
2. 在使用Iterator迭代器對集合中的元素進行迭代時,如果呼叫了集合物件的remove()方法去刪除元素之後,繼續使用迭代器遍歷元素,就會出現異常。
為了解決這個問題可以採用兩種方式:
第一種方式:找到需要刪除的元素後跳出迴圈不再迭代即可
if("Annie".equals(obj)){ list.remove(obj); break; }
第二種方式:如果需要在集合的迭代期間對集合中的元素進行刪除,可以使用迭代器本身的刪除方法。
if("Annie".equals(obj)){ it.remove() }
由此可以得出結論,呼叫迭代器物件的remove()方法刪除元素所導致的迭代次數變化,對於迭代器物件本身來說是可預知的。