JAVA常見容器

本文主要介紹JAVA中常見容器間的關係和主要區別。JAVA中的容器種類很多,且各有特點。爲此特意進行學習研究,寫下此文,作爲一點總結。若有錯誤,歡迎拍磚。
JAVA各容器繼承關係

上圖是JAVA常見的各個容器的繼承關係,我們就順着繼承關係說一下各個接口或者類的特點吧。


Iterable 接口

  • Iterable是一個超級接口,被Collection所繼承。它只有一個方法: Iterator<T> iterator() //即返回一個迭代器

  • 迭代器是一種設計模式,它是一個對象,它可以遍歷並選擇序列中的對象,而開發人員不需要了解該序列的底層結構。迭代器通常被稱爲**“輕量級”**對象,因爲創建它的代價小。

  • Java中的Iterator功能比較簡單,並且只能單向移動
      (1) 使用方法iterator()要求容器返回一個Iterator。第一次調用Iterator的next()方法時,它返回序列的第一個元素。注意:iterator()方法是java.lang.Iterable接口,被Collection繼承。
      (2) 使用next()獲得序列中的下一個元素。
      (3) 使用hasNext()檢查序列中是否還有元素。
      (4) 使用remove()將迭代器新返回的元素刪除。

  • Iterator是Java迭代器最簡單的實現,爲List設計的ListIterator具有更多的功能,它可以從兩個方向遍歷List,也可以從List中插入和刪除元素。
      舉一個例子來說明迭代器的用法:

public static void main(String args[]) {
		List<String> l = new ArrayList<String>();
		l.add("aa");
		l.add("bb");
		l.add("cc");
		Iterator iter = l.iterator();
		while(iter.hasNext()){
			System.out.println((String)iter.next());
		}
//	for循環的版本
//		for(Iterator<String> iter=l.iterator();iter.hasNext();){
//			String str = (String)iter.next();
//			System.out.println(str);
//		}  			
	}

運行結果:
運行結果

很明顯,iterator用於while循環更方便簡潔一些。


Collection 接口

我們直接打開API文檔進行查看
Collection接口 文檔中寫道,JDK 不提供此接口的任何直接 實現:它提供更具體的子接口(如 Set 和 List)實現。也就是一般不會直接使用Collection,而是會使用它的子類,如List或Set。

在圖中我標註了4點,不同的Collection子類對於有序性、重複性、null、線程同步都有不同的策略。基於此,Collection的介紹就這樣,下面就其具體的子類來進行介紹。



List 接口

  • List是有序的 collection(也稱爲序列)。此接口的用戶可以對列表中每個元素的插入位置進行精確地控制。用戶可以根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素。

  • 用戶插入的順序或者指定的位置就是元素插入的位置。它與Set不同,List允許插入重複的值

  • List 接口提供了特殊的迭代器,稱爲 ListIterator,除了允許 Iterator 接口提供的正常操作外,該迭代器還允許元素插入替換,以及雙向訪問。還提供了一個方法(如下)來獲取從列表中指定位置開始的列表迭代器。

ListIterator <E> listIterator(int index)
返回列表中元素的列表迭代器(按適當順序),從列表的指定位置開始

  • List 接口提供了兩種搜索指定對象的方法。從性能的觀點來看,應該小心使用這些方法。在很多實現中,它們將執行高開銷的線性搜索。

  • List 接口提供了兩種在列表的任意位置高效插入和移除多個元素的方法。

  • List的子類

    • List最流行的實現類有Vector、ArrayList、LinkedList。三者的區別將在下文提及。
    • 1.1) ArrayList (類)
    1. ArrayLis是基於數組實現的List類,它封裝了一個動態的、增長的、允許再分配的Object[ ]數組.它允許對元素進行快速隨機訪問
    2. 當從ArrayList的中間位置插入或者刪除元素時,需要對數組進行復制、移動、代價比較高。因此,它適合隨機查找和遍歷,不適合插入和刪除。
    • 1.2)Vector (類)
    1. Vector與ArrayList一樣,也是通過數組實現的,不同的是它支持線程的同步,即某一時刻只有一個線程能夠寫Vector,避免多線程同時寫而引起的不一致性,但實現同步需要很高的花費,因此,訪問它比訪問ArrayList。所以現在已經不太常用了
      1.2.1)Stack (類)
      Stack是Vector提供的一個子類,用於模擬"棧"這種數據結構(LIFO後進先出)
    • 1.3)LinkedList (類)
    1. LinkedList是用鏈表結構存儲數據的,很適合數據的動態插入和刪除,隨機訪問和遍歷速度比較慢。
    2. 另外,它還實現了Deque接口,專門用於操作表頭和表尾元素,可以當作堆棧、隊列和雙向隊列使用。

    關於ArrayList和Vector的區別,再次回顧一下:

    名稱 擴容方式(默認) 線程安全 速度 有效個數的屬性
    ArrayList 增長50% 線程不安全 size
    Vector 增長一倍 線程安全 elementCount
    共同點 如果新增的有效元素個數超過數組本身的長度,都會導致數組進行擴容 - remove,add(index,obj)方法都會導致內部數組進行數據拷貝的操作,這樣在大數據量時,可能會影響效率 -

寫在後面:List接口的排序可以通過Collections.sort()來進行定製排序
只需要繼承Comparable接口後,重寫compareTo()**方法
代碼如下:

	public class Student extends Thread implements Comparable {	
   private int age;
   private String name;
   private String tel;
   private int height;

   public Student( String name,int age, String tel, int height) {
   	this.age = age;
   	this.name = name;
   	this.tel = tel;
   	this.height = height;
   }

   public static void main(String args[]) throws InterruptedException{
   	Student stu1 = new Student("張三",21,"156482",172);
   	Student stu2 = new Student("李四",18,"561618",168);
   	Student stu3 = new Student("王五",19,"841681",170);
   	Student stu4 = new Student("趙七",20,"562595",180);
   	
   	List<Student> list = new ArrayList<Student>();
   	//亂序插入
   	list.add(stu3);
   	list.add(stu1);
   	list.add(stu4);
   	list.add(stu2);
   	
   	System.out.println("-----------排序前----------");
   	Iterator<Student> it = list.iterator();
   	while(it.hasNext()){
   		System.out.println(it.next());
   	}
   	/*
   	 * 使用Collections的sort方法讓集合排序,使用其方法必須要重寫
   	 * Comparable接口的compareTo()方法
   	 * */
   	Collections.sort(list);
   	System.out.println("-----------按照年齡排序後----------");
   	for(int i=0;i<list.size();i++){
   		System.out.println(list.get(i).toString());
   	}
   }

   //重寫compareTo方法,用age來比較。也可以用別的來比較
   @Override
   public int compareTo(Object o) {
   	//使用當前對象的年齡和其他對象的年齡比較,如果<0返回負數,>0返回正數,=0返回0
   	int z = this.age - ((Student)o).getAge();
   	if(z<0) 
   		return -1;  // 返回其他負數也行
   	else if(z == 0) 
   		return 0;
   	else 
   		return 1;  //返回其他正數也行
   }
   
   //重寫toString,便於輸出
   @Override
   public String toString(){
   	return name+","+age+","+tel+","+height;
   }

輸出結果如下:
在這裏插入圖片描述



Set接口

  • Set,顧名思義,集合的意思。java的集合和數學的集合一樣,滿足集合的無序性,確定性,單一性。所以可以很好的理解,Set是無序、不可重複的。同時,如果有多個null,則不滿足單一性了,所以Set只能有一個null。
  • Set類似於一個罐子,丟進Set的元素沒有先後的差別
  • Set判斷兩個對象相同不是使用"=="運算符,而是根據equals方法。也就是說,我們在加入一個新元素的時候,如果這個新元素對象和Set中已有對象進行注意equals比較都返回false,則Set就會接受這個新元素對象,否則拒絕。
    ——因爲Set的這個制約,在使用Set集合的時候,應該注意兩點:
    1. 爲Set集合裏的元素的實現類實現一個有效的equals(Object)方法;
    2. 對Set的構造函數,傳入的Collection參數不能包含重複的元素。
  • Set的子類

    • Set最流行的實現類有HashSet、TreeSet、LinkedHashSet(從HashSet繼承而來)。

    • 1.1) HashSet (類)
    1. HashSet是Set接口的典型實現,HashSet使用HASH算法來存儲集合中的元素,因此具有良好的存取和查找性能。當向HashSet集合中存入一個元素時,HashSet會調用該對象的 hashCode()方法來得到該對象的hashCode值,然後根據該HashCode值決定該對象在HashSet中的存儲位置。
    2. 值得主要的是,HashSet集合判斷兩個元素相等的標準是兩個對象通過equals()方法比較相等,並且兩個對象的hashCode()方法的返回值相等
      1.1.1)LinkedHashSet(類)
      1. LinkedHashSet集合也是根據元素的hashCode值來決定元素的存儲位置,但和HashSet不同的是,它同時使用鏈表維護元素的次序,這樣使得元素看起來是以插入的順序保存的。
      2. 當遍歷LinkedHashSet集合裏的元素時,LinkedHashSet將會按元素的添加順序來訪問集合裏的元素。
      3. LinkedHashSet需要維護元素的插入順序,因此性能略低於HashSet的性能,但在迭代訪問Set裏的全部元素時(遍歷)將有很好的性能(鏈表很適合進行遍歷)LinkedHashSet需要維護元素的插入順序,因此性能略低於HashSet的性能,但在迭代訪問Set裏的全部元素時(遍歷)將有很好的性能(鏈表很適合進行遍歷)
    • 1.2) SortedSet (接口):
      此接口主要用於排序操作,實現了此接口的子類都屬於排序的子類
      • 1.2.1)TreeSet(類)
      • TreeSet是SortedSet接口的實現類,TreeSet可以確保集合元素處於排序狀態

    • 1.3) EnumSet (類)
    • EnumSet是一個專門爲枚舉類設計的集合類,EnumSet中所有元素都必須是指定枚舉類型的枚舉值,該枚舉類型在創建EnumSet時顯式、或隱式地指定。EnumSet的集合元素也是有序的,


Queue 接口

此接口用於模擬“隊列”數據結構(FIFO)。新插入的元素放在隊尾,隊頭存放着保存時間最長的元素。

  • Queue的子類、子接口

    • 1.1) PriorityQueue—— 優先隊列(類)
    • 其實它並沒有按照插入的順序來存放元素,而是按照隊列中某個屬性的大小來排列的。故而叫優先隊列。

    • 1.2) Deque——雙端隊列(接口)
      • 1.2.1)ArrayDeque(類)
        基於數組的雙端隊列,類似於ArrayList有一個Object[] 數組。
      • 1.2.2)LinkedList (類)(上文已有,略)

    簡單回顧一下上述三個接口的區別
容器名 是否有序 是否可重複 null的個數
List 有序 可重複 允許多個null
Set 無序 不可重複 只允許一個null
Queue 有序(FIFO) 可重複 通常不允許插入null


Map 接口

  • Map不是collection的子接口或者實現類。Map是一個接口。

  • Map用於保存具有“映射關係”的數據。每個Entry都持有鍵-值兩個對象。其中,Value可能重複,但是Key不允許重複(和Set類似)。

  • Map可以有多個Value爲null,但是只能有一個Key爲null。

  • Map的子類、子接口

  • 1) HashMap (類)

    和HashSet集合不能保證元素的順序一樣,HashMap也不能保證key-value對的順序。並且類似於HashSet判斷兩個key是否相等的標準一樣: 兩個key通過equals()方法比較返回true、 同時兩個key的hashCode值也必須相等

    • 1.1) LinkedHashMap (類)
    • LinkedHashMap也使用雙向鏈表來維護key-value對的次序,該鏈表負責維護Map的迭代順序,與key-value對的插入順序一致(注意和TreeMap對所有的key-value進行排序區分)。


  • 2) HashTable (類)

    是一個古老的Map實現類。

    • 2.1) Properties(類)
    • Properties對象在處理屬性文件時特別方便(windows平臺的.ini文件)。Properties類可以把Map對象和屬性文件關聯,從而把Map對象的key - value對寫入到屬性文件中,也可把屬性文件中的“屬性名-屬性值”加載進Map對象中。

  • 3) SortedMap(接口)

    如同Set->SortedSet->TreeSet一樣,Map也有Map->SortedMap->TreeMap的繼承關係。

    • 3.1) TreeMap(類)
    • TreeMap是一個紅黑樹結構,每個鍵值對都作爲紅黑樹的一個節點。TreeMap存儲鍵值對時,需要根據key對節點進行排序,TreeMap可以保證所有的key-value對處於有序狀態。 同時,TreeMap也有兩種排序方式:自然排序、定製排序(類似於上面List的重寫CompareTo()方法)。
  • 4) WeakHashMap(類)
    • 看名字就能發現,這是Weak後的HashMap。但是二者大體差別不大。
    • 區別在於,HashMap的key保留了對實際對象的強引用,這意味着只要該HashMap對象不被銷燬,該HashMap所引用的對象就不會被垃圾回收。
    • 但WeakHashMap的key只保留了對實際對象的弱引用,這意味着如果WeakHashMap對象的key所引用的對象沒有被其他強引用變量所引用,則這些key所引用的對象可能被垃圾回收,當垃圾回收了該key所對應的實際對象之後,WeakHashMap也可能自動刪除這些key所對應的key-value對
  • 5) IdentityHashMap(類)
    • 這個類也和HashMap類似(怎麼那麼多類似的hhhh),區別在於,在IdentityHashMap中,當且僅當兩個key嚴格相等(key1==key2)時,IdentityHashMap才認爲兩個key相等
  • 6) EnumMap(類)
    • EnumMap是一個與枚舉類一起使用的Map實現,EnumMap中的所有key都必須是單個枚舉類的枚舉值。創建EnumMap時必須顯式或隱式指定它對應的枚舉類。EnumMap根據key的自然順序存儲。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章