List集合知識總結
在程式設計過程中,會很頻繁的使用集合,集合的相關知識也非常重要,也是每一個開發人員必須掌握的知識。
一:集合的概念
集合:儲存數量不確定的資料,以及儲存具有對映關係的資料的容器,簡單的理解就是用於儲存數量不等的多個物件的容器。
集合和陣列不一樣,陣列元素既可以是基本型別的值,也可以是物件(物件的引用變數);而集合裡只能儲存物件(物件的引用變數)。
Java集合類主要由兩個集合框架的根介面派生而出:Collection和Map
Java中Collection介面的體系機構:
Collection介面和Iterator介面:
Collection介面是List、Set和Queue介面的父介面,該介面中定義了一些操作集合元素的方法:
下面列舉一些常見的方法:
boolean add(Object o):該方法用於向集合裡新增一個元素。
boolean addAll(Collection c):該方法把集合c裡的所有元素新增到指定的集合裡。
void clean():清除集合裡的所有元素,將集合長度變為0。
boolean contains():返回集合裡是否包含指定元素。
boolean containsAll():返回集合裡是否包含集合c裡的所有元素。
boolean isEmpty():返回集合是否為空。當集合長度為0時返回true,否則返回false。
Iterator iterator():返回一個Iterator物件,用於遍歷集合中的元素。
boolean remove(Object o):刪除集合中指定的元素o,當集合中包含了一個或多個元素o時,這些元素將被刪除,該方法將返回true。
boolean removeAll(Collection c):從集合中刪除集合c裡包含的所有元素。
boolean retainAll(Collection c):從集合中刪除集合c裡不包含的元素(相當於取得把呼叫該方法的集合變為該集合和集合c的交集),如果該操作改變了呼叫該方法的集合,返回true。
int size():該方法返回集合裡元素的個數。
Object[] toArray():該方法把集合轉換成一個數組,所有集合元素變成對應的陣列元素。
Iterator介面:
Iterator介面也是Java集合框架的成員,但它與Collection系列、Map系列的集合不一樣:Collection系列集合和Map系列的集合主要用於盛裝其他物件,而Iterator則主要用於遍歷集合的元素。又叫迭代器。
Iterator介面隱藏了各種Collection實現類的底層細節,只嚮應用程式提供遍歷集合元素的統一程式設計介面。
Iterator介面定義了三個方法:
boolean hasNext():如果被迭代的集合元素還有沒遍歷,則返回true。
Object next():返回集合裡的下一個元素。
void remove():刪除集合裡上一次next方法返回的元素。
Iterator僅用於遍歷集合,如果需要建立Iterator物件,則必須有一個被迭代的集合。沒有集合和Iterator彷彿無本之木,沒有存在的意義。
當使用Iterator對集合元素進行迭代時,Iterator並不是把集合元素本事傳給了迭代變數,而是把集合元素的值傳給了迭代變數,所以修改迭代變數的值對集合元素本事沒有影響。
當使用Iterator來迭代訪問Collection集合元素時,Collection集合裡的元素不能被修改,只能通過remove方法刪除上次next方法返回的集合元素才可以,否則報java.util.ConcurrentModificationException異常。
Iterator迭代器採用的是快速失敗(fail-fast)機制,一旦在迭代過程中檢測到該集合已經被修改(通常是其他執行緒進行修改),程式將報java.util.ConcurrentModificationException異常。而不是顯示修改後的結果,這樣可以避免共享資源而引發的問題。
foreach迴圈變數集合元素
格式:
for(型別 新物件:集合){
}
用法和Iterator類似。
二:Set介面
Set集合是無順序的,其集合元素不允許重複。
Set集合判斷兩個物件相同不是使用==運算子,而是根據equals方法。簡單的說,如果只要兩個物件用equals方法比較返回true,Set集合就不會接受這兩個物件;反之,則成立。
HashSet類:
HashSet是Set集合的實現類,HashSet按Hash演算法來儲存集合中的元素,因此具有很好的存取和查詢效能。
HashSet集合特點:
(1)不能保證元素的排列順序,順序有可能發生變化。
(2)HashSet不是同步的,如果多個執行緒同時訪問一個HashSet,如果有2條或2條以上執行緒同時修改HashSet集合時,必須通過程式碼實現執行緒同步。
(3)集合元素值可以是null。
向HastSet集合中存入一個元素時,HashSet會呼叫該物件的hashCode()方法來得到該物件的hashCode值,然後根據該HashCode值來決定該物件在HashSet中儲存位置。如果有兩個元素通過equals方法比較返回true,但它們的hashCode()方法返回值不相等,HashSet將會把它們儲存在不同的位置,就可以新增成功(簡單的理解HastSet集合判斷兩個元素是否相等的標準是兩個物件通過equals方法比較相等,且兩個物件的hashCode()方法返回的值也要相等)。
TreeSet類:
TreeSet是SortedSet介面的唯一實現,TreeSet可以確保集合元素處於排序狀態。
TreeSet新添的方法:
(1)Comparator comparator():返回當前Set使用的Comparator,或者返回null,表示以自然方式排序。
(2)Object first():返回集合中的第一個元素。
(3)Object last():返回集合中的最後一個元素。
(4)Object lower(Object e):返回集合中位於指定元素之前的元素。
(5)Object higher(Object e):返回集合中位於指定元素之後的元素。
(6)SortedSet subSet(fromElement,toElement):返回此Set的子集合,範圍衝fromElement(包含)到toElement(不包含)。
(7)SortedSet headSet(toElement):返回此Set的子集,由小於toElement的元素組成。
(8)SortedSet tailSet(fromElement):返回此Set的子集,由大於或等於fromElement的元素組成。
TreeSet集合並不是根據元素的插入順序進行排序,而是根據元素實際值進行排序。
TreeSet集合採用紅黑樹的資料結構對元素進行排序。TreeSet集合支援兩種排序方法:自然排序和定製排序。預設使用自然排序。
自然排序
TreeSet呼叫集合元素的compareTo(Object obj)方法來比較元素之間大小關係,然後將集合元素按升序排列,這就是自然排序。
Java提供了一個Comparable介面,該接口裡定義了一個compareTo(Object obj)方法,該方法返回一個整數,實現該介面的類必須實現該方法,當一個物件呼叫該方法與另一個物件進行比較時,比如:obj1.compareTo(obj2),如果該方法返回0,則表明兩個物件相等,如果返回是一個正整數,則表明obj1大於obj2,如果返回是一個負數,則表明obj1小於obj2。
注意:向TreeSet集合中新增元素時,只有第一個元素可以不實現Comparable介面,後面新增的所有元素都必須實現Comparable介面。否則報ClassCastException異常。
當把一個物件加入TreeSet集合中時,TreeSet呼叫該物件的compareTo方法與集合中的其他物件比較大小,然後根據紅黑樹演算法決定它儲存的位置。如果兩個物件相等,TreeSet將把他們儲存在同一位置。TreeSet集合比較兩個物件相等的標準是:兩個物件通過equals方法返回true,或通過compareTo方法比較返回0,則認為兩個物件是同一個物件。
定製排序
通過實現Comparator介面中的compare方法來實現集合的定製排序,
int compare(T o1,T o2)方法比較大小,如果返回是正整數,則表明o1大於o2,如果返回0,則表明兩個物件相等,如果返回負數,則表明o1小於o2。
如果需要實現定製排序,則需要在建立TreeSet集合物件時,並提供一個Comparator物件與該集合關聯,由該Comparator物件負責集合的排序邏輯。
經典例項
public class TreeSetTest {
public static void main(String[] args) {
//定義TreeSet集合,併為集合提供一個排序的Comparator物件(這裡是匿名內部類)。
TreeSet<M> ts = new TreeSet<M>(new Comparator<M>() {
@Override
public int compare(M m1, M m2) {
// TODO Auto-generated method stub
if (m1.num > m2.num) {
return -1;//這是按降序排序,如果想按升序排序,這裡返回正整數,下面else中返回負數,即可。
} else if (m1.num == m2.num) {
return 0;
} else {
return 1;
}
}
});
//向集合中新增資料
ts.add(new M(5));
ts.add(new M(-3));
ts.add(new M(8));
ts.add(new M(6));
System.out.println(ts);
}
}
class M {
// 定義一個變數
int num;
public M(int num) {
// 建構函式中為變數賦值
this.num = num;
}
// 重寫toString方法
public String toString() {
return "M物件num的值為" + this.num;
}
}
EnumSet類
EnumSet類是一個專為列舉類設計的集合類,EnumSet中所有的元素都必須是指定列舉型別的列舉值,該列舉型別在建立EnumSet時顯式或隱式地指定。EnumSet的集合元素也是有序的,EnumSet以列舉值在Enum類內的定義順序來決定集合元素的順序。EnumSet集合不允許插入null元素。
EnumSet集合的內部一位向量的形式儲存,這種儲存形式非常緊湊、高效,因此EnumSet物件佔用記憶體很小,而且執行效率很好。具體體現在批量操作集合時。
EnumSet類沒有暴露任何建構函式來建立該類的例項,程式通過它提供的static方法來建立EnumSet物件。
EnumSet集合常用的方法
(1)static EnumSet allOf(Class elementType):建立一個包含指定列舉類裡所有列舉值的EnumSet集合。
(2)static EnumSet complementOf(EnumSet s):建立一個其元素型別與指定EnumSet裡元素型別相同的EnumSet,新EnumSet集合包含原EnumSet集合所不包含的、此列舉類剩下的列舉值。
(3)static EnumSet copyOf(Collection c):使用一個普通集合來建立EnumSet集合。
(4)static EnumSet copyOf(EnumSet s):建立一個與指定EnumSet具有相同元素型別、相同集合元素的EnumSet。
(5)static EnumSet noneOf(Class elementType):建立一個元素型別為指定列舉型別的空EnumSet。
(6)static EnumSet of(E first,E...rest):建立一個包含一個或多個列舉值的EnunSet,傳入的多個列舉值必須屬於同一個列舉類。
(7)static EnumSet range(E from,E to):建立包含從from列舉值,到to列舉值範圍內所有列舉值的EnumSet集合。
經典例項:
public class EnumSetTest {
public static void main(String[] args) {
EnumSet<Season> es=EnumSet.allOf(Season.class);
System.out.println(es);//列印[SPRING, SUMMER, FAILL, WINTER]
EnumSet<Season> es1=EnumSet.of(Season.SPRING,Season.SUMMER,Season.WINTER);
System.out.println(es1);//列印[SPRING, SUMMER, WINTER]
EnumSet<Season> es2=EnumSet.range(Season.SUMMER, Season.WINTER);
System.out.println(es2);//列印[SUMMER, FAILL, WINTER]
EnumSet<Season> es3=EnumSet.complementOf(es2);
System.out.println(es3);//列印[SPRING]
}
}
enum Season{
SPRING,SUMMER,FAILL,WINTER
}
總結
HashSet和TreeSet是Set的兩個典型實現,HashSet的效能總比TreeSet好(新增,查詢操作),因為TreeSet需要額外的紅黑樹演算法來維護集合元素的次序。TreeSet是有序的集合。
HashSet還有一個子類LinkedHashSet,對於普通的插入,刪除操作,LinkedHashSet比HashSet要慢一點點,這是因為維護連結串列所帶來的開銷。不過遍歷集合時,LinkedHashSet就非常塊 。
EnumSet是所有Set實現類中效能最好 ,但它只能儲存同一個列舉類的列舉值作為集合元素。
注意:Set的三個實現類HashSet 、TreeSet、EnumSet都是執行緒不安全的。如果有多條執行緒同事訪問一個Set集合,並且有超過一條執行緒修改集合的值,則必須手動保證集合的同步性,否則將無法訪問修改後的集合。
List介面
List集合代表一個有序的集合 ,集合中每個元素都有其對應的順序索引。List集合允許使用重複元素,通過索引來訪問指定位置的集合元素。
List集合在Collection的基礎上新新增的方法:
(1)void add (int index,Object element):將元素element插入在List集合的index處。
(2)boolean addAll(int index,Collection c):將集合c所包含的所有元素都插入在List集合的Index處。
(3)Object get(int index):返回集合index索引處的元素。
(4)int indexOf(Object o):返回物件o在List集合中出現的位置索引。
(5)int lastIndexOf(Object o):返回物件o在List集合中最後一次出現的位置索引。
(6)Object remove(int index):刪除並返回index索引處的元素。
(7)Object set(int index,Object element):將index索引處的元素替換成element物件,返回新元素。
(8)List subList(int fromIndex,int toIndex):返回從索引fromIndex(包含)到索引toIndex(不包含)處所有集合元素組成的子集合。
ListIterator介面:
ListIterator介面是Iterator的子介面,提供了專門操作List的方法。
ListIterator介面新增的方法:
(1)boolean hasPrevious():返回該迭代器關聯的集合是否還有上一個元素。
(2)Object previous():返回該迭代器的上一個元素。
(3)void add():在指定位置插入一個元素。
根據上面的三個方法和解釋,不難發現ListIterator新增加了向前迭代的功能,而且ListIterator還可以通過add方法向List集合中新增元素。
經典例項:
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("smarhit");
list.add("heima");
list.add("sichuan");
list.add("chengdu");
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
System.out.print(lit.next()+"\t");
}
System.out.println("\n----------開始反向迭代--------------");
while (lit.hasPrevious()) {
System.out.print(lit.previous()+"\t");
}
/*
* 列印的結果是:
* smarhit heima sichuan chengdu
* ----------開始反向迭代--------------
* chengdu sichuan heima smarhit
*/
}
}
ArrayList和Vector實現類
ArrayList和Vector類都是基於陣列實現的List類,ArrayList和Vector類封裝了一個動態在分配的Object[ ]陣列。每個ArrayList或Vector物件有一個capacity屬性,該屬性表示他們所封裝的陣列的長度,當想ArrayList或Vector中新增元素時,它們的capacity會自動增加。對於通常程式設計中,程式設計師無須關心ArrayList或Vector的capacity屬性。但如果向ArrayList集合或Vector集合中新增大量元素時,可使用ensureCapacity方法一次性的增加capacity。這樣可以提高效能。
ArrayList和Vector在用法上幾乎完全相同(Vector是古老的集合,JDK1.0就存在了),但ArrayList集合是執行緒不安全的,當多條執行緒訪問同一個集合時,如果有超過一條執行緒修改了ArrayList集合,則程式必須手動保證該集合的同步性。而Vector集合則是執行緒安全的。無須程式保證集合的同步性。所以Vector的效能比ArrayList的效能低。
Vector還提供了一個Stack子類,它用於模擬了“棧”這中資料結構,“棧”通常是指“後進先出”的容器。最後“push”進棧的元素,將最先被“pop”出棧。
Stack類提供的方法:
(1)Object peek():返回“棧”的第一個元素,但並不將該元素"pop"出棧。
(2)Object pop():返回"棧"的第一個元素,並將該元素"pop"出棧。
(3)void push(Object item):將一個元素“push”進棧,最後一個進“棧”的元素總是位於“棧”頂。
經典例項:
public class StackTest {
public static void main(String[] args) {
Stack<String> s=new Stack<String>();
s.push("smarhit");
s.push("beijin");
s.push("neijiang");
s.push("chengdu");
System.out.println(s);//列印[smarhit, beijin, neijiang, chengdu]
System.out.println(s.peek());//列印chengdu
System.out.println(s);//列印[smarhit, beijin, neijiang, chengdu]
System.out.println(s.pop());//列印chengdu
System.out.println(s);//列印[smarhit, beijin, neijiang]
}
}
固定長度的List
陣列的工具類 Arrays提供的asList()方法是把一個數組或指定個數的物件轉換成一個List集合, 但這個List集合既不是ArrayList實現類的例項,也不是Vector實現類的例項,而是Arrays的內部類ArrayList例項。Arrays.ArrayList是一個固定長度的List集合,程式只能遍歷訪問該集合裡的元素,不可增加,刪除該集合中的元素。
Queue介面
Queue模擬了佇列這中資料結構,佇列通常是"先進先出"的容器。
Queue介面提供的方法:
(1)void add(Object e):將指定元素加入此佇列的尾部。
(2)Object element():獲取佇列頭部的元素,但不是刪除該元素。
(3)boolean offer(Object e):將指定元素加入此佇列的尾部。當使用有容器限制的佇列時,此方法通常比add(Object e)方法更好。
(4)Object peek():獲取佇列頭部的元素,但是不刪除該元素。如果此佇列為空,則返回null。
(5)Object poll():獲取佇列頭部的元素,並刪除該元素。如果此佇列為空,則返回null。
(6)Object remove():獲取佇列頭部元素,並刪除該元素。
LinkedList實現類:
LinkedList類是一個比較特殊的類,它實現了List介面,還實現了Deque介面,Deque介面是Queue介面的子介面,它代表一個雙向佇列。
LinkedList類提供的方法:
(1)void addFirst(Object e):將指定元素插入該雙向佇列的開頭。
(2)void addLast(Object e):將指定元素插入該雙向佇列的尾部。
(3)Iterator descendingIterator():返回以該雙向佇列對應的迭代器,該迭代器將以逆向順序來迭代佇列中的元素。
(4)Object getFirst():獲取、但不刪除雙向佇列的第一個元素。
(5)Object getLast():獲取、但不刪除雙向佇列的最後一個元素。
(6)boolean offerFirst(Object e):將指定的元素插入該雙向佇列的開頭。
(7)boolean offerLast(Object e):將指定的元素插入該雙向佇列的尾部。
(8)Object peekFirst():獲取、但不刪除該雙向佇列的第一個元素;如果此雙向佇列為空,則返回null。
(9)Object peekLast():獲取、但不刪除該雙向佇列的最後一個元素;如果此雙向佇列為空,則返回null。
(10)Object pollFirst():獲取、並刪除該雙向佇列的第一個元素;如果此雙向佇列為空,則返回null。
(11)Object pollLast():獲取、並刪除該雙向佇列的最後一個元素;如果此雙向佇列為空,則返回null。
(12)Object pop():pop出該雙向佇列所表示的棧中第一個元素。
(13)void push(Object e):將一個元素push進該雙向佇列所表示的棧中。
(14)Object removeFirst():獲取、並刪除該雙向佇列的第一個元素。
(15)Object removeFirstOccurrence(Object o):刪除該雙向佇列的第一個出現元素o。
(16)removeLast():獲取、並刪除該雙向佇列的最後一個元素。
(17)removeLastOccurrence(Object o):刪除該雙向佇列的最後一次出現元素o。
總結:LinkedList與ArrayList、Vector的實現機制完全不同。ArrayList、Vector內部以陣列的形式來儲存集合中的元素,因此隨機訪問集合元素上有較好的效能;而linkedList內部以連結串列的形式來儲存集合中的元素,因此隨機方法集合元素時效能較差,但在插入、刪除元素時效能非常出色(只需改變指標所指的地址即可)。
PriorityQueue實現類
PriorityQueue是一個比較標準的佇列實現類,PriorityQueue儲存佇列元素的順序並不是按加入佇列的順序,而是按佇列元素的大小進行重新排序。
PriorityQueue不允許插入null元素,它需要對佇列元素進行排序。
排序的方式:自然排序,定製排序。
上面是對List集合的詳解總結。在開發過程中,我們可能只運用到一些常見的結合,但其他集合也應做相應的瞭解。