1. 程式人生 > >JavaSE—合集框架(新)

JavaSE—合集框架(新)

一、什麼是合集?

答:Java的合集框架支援以下兩種型別的容器:

       一種是為了儲存一個元素合集,簡稱為合集(collection);

       另一種是為了儲存鍵值對,稱為對映表(map),對映表使用一個鍵,快速搜尋其對應的值,非常高效;

二、合集包含哪些內容?

答:Set用於儲存一組不允許重複,無序的元素合集;

       List用於儲存允許重複,有序(指的是順序儲存元素)的元素合集;

       Stack(棧類)用於儲存採用先進後出的方式處理的物件;

       Queue(佇列)用於儲存採用先進先出的方式處理的物件;

       Priority Queue用於儲存按照優先順序順序處理的物件;

三、合集框架的結構

答:以上的合集均是在介面中定義通用特性,之後由對應的抽象類來實現這些介面並提供部分實現,最後再由對應的子類用具體的資料結構實現介面;

四、Collection和Collections的區別

答:這裡有一個基本的概念非常容易混淆:

       Collection是位於 java.util 下的介面,處理物件合集的根介面,也就是所有合集框架的父介面;

       Collections是 java.util 下的一個類,它包含各種有關合集操作的靜態方法;

五、合集根介面—Collection

答:Collection介面是處理物件合集的根介面,除了add,size,iterator方法之外(這些方法會在合適的子類中實現0),Abstract-Collection類實現了該介面中的所有方法;

注意: 

list1.retainAll(list2);
//執行上述命令後,list1中只會剩下list1和list2都有的元素

六、合集Cloneable介面和Serializable介面的支援

答:除了java.lang.PriorityQueue沒有實現Cloneable介面外,合集框架中的其他所有具體類都實現了java.lang.Cloneable和java.lang.Serializable介面,所以除了優先佇列外,所有的Cloneable的例項都是可克隆和序列化的;

七、迭代器和listIterator

public class Demo {
	public static void main(String[] args) {
		// 此處不能用Collection介面來宣告集合,因為listIterator方法是List介面中的方法
		List<String> list = new ArrayList<>();
		list.add("公子羽");
		list.add("明月心");
		list.add("百曉生");
		list.add("葉孤城");
		// 正向迭代器
		Iterator<String> iterator = list.iterator();
		while (iterator.hasNext()) {
			System.out.print(iterator.next() + "\t");
		}
 
		System.out.println();
		/**
		 * 反向迭代器,此處必須給定迭代器開始迭代的位置,也就是將游標移到開始迭代的元素處 
		 * 下一個元素的索引為即將輸出的元素的索引
		 * 上一個元素的索引為即將輸出的元素的前一個元素的索引
                 * 雖然可以使用get(i)方法進行遍歷,但由於效率低下,並不推薦
                 * foreach迴圈底層也是隱式的運用了iterator
		 */
		ListIterator<String> listIterator = list.listIterator(list.size());
		while (listIterator.hasPrevious()) {
			System.out.println("下一個元素的索引:" + listIterator.nextIndex());
			System.out.println("上一個元素的索引" + listIterator.previousIndex());
			System.out.println(listIterator.previous() + "\t");
		}

	}
}

注意:

        hasNext方法用於向前遍歷是確認是否還有下一個元素,hasPrevious方法用於向後遍歷時確認是否還有上一個元素;

        next方法返回的是迭代器中的下一個元素,previous方法返回的是迭代器中的前一個元素;

        nextIndex用於返回下一個元素的下標,previousIndex用於返回上一個元素的下標;

listIterator中包含的方法:

八、線性表介面—List

答:1、List介面實現了Collection介面,它的兩個具體子類:陣列線性表ArrayList 和 連結串列類LinkedList 來建立一個線性表(list);

        2、List介面增加了面向位置的操作,並且增加了一個能夠雙向遍歷線性表的新線性表迭代器(參考迭代器中的例子);

九、陣列(順序)線性表—ArrayList類

答:ArrayList類底層使用陣列儲存元素,這個陣列是動態建立的,大小是可變的,如果元素的數量超過了陣列的容量,就建立一個更多的陣列,並將當前陣列中的所有元素都複製到新陣列中;

十、連結串列線性表—LinkedList類

答:ArrayList對陣列進行了封裝,實現了長度可變的陣列,它的優點在於遍歷和隨機訪問元素的效率比較高;

       LinkedList採用連結串列儲存方式,優點在於插入、刪除時效率比較高;

       LinkedList除了List介面中定義的方法之外,還提供了從線性表兩端提取、插入、刪除元素的方法

十一、向量類—Vector類

答:Vector和ArrayList都是AbstractList的子類,Vector是棧類Stack的父類,除了包含訪問和修改向量的同步方法之外,Vector和ArrayList是一樣的。同步方法用於防止兩個或多個執行緒同時訪問和修改某個向量時引起資料的損壞。

對於不需要同步的應用程式來說,使用ArrayList比使用Vector效率更高

Vector也屬於線性表,並且它實現的陣列是一個動態陣列。

Vector類有如下四個構造方法:

//預設向量,預設大小為10
Vector<Object> v = new Vector<>();
//建立指定大小的向量
Vector<Object> v = new Vector<>(int size);
//建立指定大小和增量的向量
Vector<Object> v = new Vector<>(int size,int incr);
//建立一個包含集合c的向量
Vector<Object> v = new Vector<>(Collection c);

具體類中的方法不再贅述,可自行查閱。

十二、佇列—Queue介面

答:佇列是一種先進先出的資料結構,元素被追加到佇列末尾,然後從佇列頭開始刪除。

Queue繼承自Collection,方法如下:

十三、雙端佇列Deque和連結串列LinkedList

答:LinkedList實現了Deque介面,Deque介面繼承自Queue介面,因此可以用LinkedList建立一個佇列。

LinkedList很適合用於進行佇列操作,因為它可以高效的線上性表的兩端插入和刪除元素。

十四、優先佇列—PriorityQueue類

答:PriorityQueue實現了一個優先佇列,預設情況下,優先佇列使用Comparable以元素的自然順序進行排序,用小最小數值的元素擁有最高的優先順序,最先被從佇列中刪除,如果優先順序一樣,則任意選一個,也可以在構造方法中指定一個比較器,然後按照比較器順序進行排序後,以比較器的順序刪除佇列。

十五、Set介面

答:實現Set的具體類必須確保不能向這個集合新增重複的元素。

Set介面的三個具體類是:雜湊類HashSet,鏈式雜湊集LinkedHashSet,和樹形集TreeSet。

因為在Set集合中儲存的物件是無序且唯一的,所以所有的需要索引的方法,Set集合都沒有。

如果不需要維護元素的順序,那麼HashSet將是效率最高的。

十六、雜湊類—HashSet

答:HashSet類是一個實現了Set介面的具體類,有以下三個構造方法:

//預設構造方法,初始容量為16,負載係數0.75f
HashSet<Object> a=new HashSet<>();
//以集合c建立雜湊集
HashSet<Object> a=new HashSet<>(Collection c);
//指定容量,負載係數預設0.75f
HashSet<Object> a=new HashSet<>(int size);
//指定容量和負載係數,負載係數範圍0~1.0f
HashSet<Object> a=new HashSet<>(int size,float loadFactor);

當元素個數超過了容量與負載係數的乘積,容量會自動翻倍擴容。

比較高的負載係數會降低空間開銷,但會增加查詢時間,而預設的0.75f的負載係數是對時間開銷和空間開銷一個很好的權衡。

HashSet中儲存的元素,不會按照他們儲存的順序進行儲存,如果要強加給他們一個順序,請使用LinkedHashSet

十七、鏈式雜湊集LinkedHashSet

答:LinkedHashSet是HashSet的子類,是用一個連結串列實現來拓展HashSet,它支援對集合內的所有元素按照插入集合的順序排序。

LinkedHashSet的構造方法類似與HashSet。

LinkedHashSet保持了元素插入集合的順序,如果要強加一個不同的順序(例如升序或降序),可以參照TreeSet。

十八、樹形集TreeSet

答:SortedSet是Set的一個子介面,TreeSet類實現了這個介面,它可以保證集合中的元素是有序的。

SortedSet介面提供了方法first和last以返回集合中第一個和最後一個元素,還有方法headSet(toElement)和tailSet(fromElement)以分別返回集合中元素小於toElement和大於或等於fromElement的那一部分。

NavigableSet拓展了SortSet,並提供了導航方法lower,floor,ceiling和higher分別以返回小於,小於或等於,大於或等於,以及大於一個給定元素的元素。方法pollFirst和pollLast會分別刪除並返回樹形集的第一個和最後一個元素。

如果要給元素進行制定排序方式,需要實現Comparable或者Comparator介面。

可以讓需要排序的類實現Comparable介面,之後重寫其中的compareTo方法,來為TreeSet集合制定排序方式。

或者可以單獨建立一個類實現Comparator介面,重寫compare方法,讓該類來作為比較器,如果是這種方式的話,建立TreeSet集合必須使用如下方式:

//new CompareObject為比較器類
Set<Object> set=new TreeSet<>(new CompareObject);

十九、對映表

答:可以使用三個具體的類來建立一個對映表:三列對映表HashMap,鏈式雜湊對映表LinkedHashMap,樹形對映表TreeMap。

對映表是一對利用鍵/值對儲存元素的容器,它提供了通過鍵快速獲取,刪除或更新鍵值對的功能。

對映表中的鍵可以是任意型別的物件,並且鍵是不能夠重複的,每個鍵都對應一個值。

Map介面提供了查詢、更新和獲取合集的值和合集的鍵的方法,如下圖所示:

注意:

當使用put方法在對映表中新增新的條目時,如果原先就已經存在了一個同名的鍵的鍵值對條目,那麼該條目的值會被put方法中傳入的新值所替代,並且返回與這個鍵相關聯的原來的值。

方法entrySet返回該對映表中所有條目的集合,這些條目是介面Map.Entry<k,v>的例項,該介面是Map介面的一個內部介面:

HashMap中的條目時沒有順序的,LinkedHashMap可以讓元素按照插入順序進行排序,也可以按照它們被最後一次訪問時候的順序,從最早到最晚排序,這稱為訪問順序排序。無參構造方法是按照插入順序來建立LinkedHashMap物件的,要按照訪問順序建立,需要使用如下的構造方法:

//將flag設定為true,即是按照訪問順序建立對映表
LinkedHashMap<Object, Object> linkedHashMap = new LinkedHashMap<>(int size, float loadFactor, boolean flag);

TreeMap類對於遍歷已經排好序的鍵時是很高效的,可以讓鍵的類實現Comparable介面,重寫compareTo方法來排序,也可以使用外部比較器Comparator介面(Comparator介面中的compare方法)來排序,如果要使用比較器,必須使用指定比較器的構造方法來建立對映表,不再贅述;

另外在Java2以前,都是使用Hashtable來對映建和值,為了適應java合集框架,Hashtable進行了重新設計,但是,為了向後相容保留了所有的方法,Hashtable實現了Map介面,除了Hashtable的更新方法是同步的以外,它與HashMap的用法是一樣的。

二十、如何選取合適的對映表來儲存資料呢?

答:如果不需要維護對映表中的鍵值對順序,就用HashMap;

       如果需要保證對映表中的插入順序或者訪問順序,就使用LinkedHashMap;

       如果需要對映表按照鍵排序,那就使用TreeMap。

二十一、如何選取合適的合集來儲存資料呢?

答:稍後補圖