1. 程式人生 > >List集合知識總結

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集合的詳解總結。在開發過程中,我們可能只運用到一些常見的結合,但其他集合也應做相應的瞭解。