1. 程式人生 > >Java集合框架簡述

Java集合框架簡述

Java集合框架實現了線性表、連結串列和雜湊表這幾類資料結構,為我們在程式開發帶來了許多便捷。Java集合框架分為兩部分:1.集合,用於存數一個元素集合;2.圖,用來儲存鍵值對。該文主要對JDK中Collection和Map兩個介面中進行簡述。

一、Collection介面

Java集合框架中主要支援三種類型的集合:1.Set(規則集) 2.List(線性表) 3.Queue(佇列),其層級關係如圖:


附:

Iterator介面

Iterator介面提供了為不同型別集合中的元素進行遍歷的統一方法。通過Collection介面中的iterator方法可以返回一個Iterator介面的例項(迭代器)。該例項可以通過hasNext()方法檢測迭代器中是否有更多的元素,next()方法順序訪問集合的元素等。 注:java集合框架中的所有具體類中都實現了Cloneable和Serializable介面,因此它們的例項都是可複製且可序列化的。

1、規則集Set

Set介面擴充套件於Collection介面,在一個實現Set的類中必須確保該規則集沒有相同的元素。Set介面中有三個具體類:雜湊集HashSet、鏈式雜湊集LinkedHashSet和樹形集TreeSet。

HashSet

HashSet擴充套件於Set介面,可以用來儲存互不相同的元素。當程式向HashSet的例項中新增多個相同的元素時,只有一個元素會被儲存,因為規則集中只能儲存不同的元素。此外,HashSet例項中儲存的元素沒有特定的順序,並不會按照插入順序進行排序。

主要構造方法:

+HashSet()  //無參構造,建立一個HashSet例項
+HashSet(Collection<? extends E> c)  //從集合c中建立HashSet例項
+HashSet(int size) //建立容量為size的HashSet例項

LinkedHashSet

LinkedHashSet用一個連結串列實現來擴充套件HashSet類,因此依舊無法新增重複的元素。在HashSet中,我們無法對其例項的元素進行排序,而當我們需要對元素插入的順序進行排序時,LinkedHashSet是一個可用的選擇。

主要構造方法:
+LinkedHashSet()  //建立一個無參例項
+LinkedHashSet(Collection<? extends E> c) //從集合c中建立LinkedHashSet例項
+LinkedHashset(int size) //建立一個容量為size的例項

TreeSet

TreeSet是SortSet介面中的一個具體子類,其中SortSet為Set的子介面。在LinkedHashSet中,可以通過元素插入的順序對元素排序,但是有時候需要自定義元素排序的順序,在TreeSet中,只要物件可比較,即可新增進樹形集中,並且可通過以下兩種方式進行排序: 1.使用Comparable介面實現。當插入的物件為Comparable例項(如String,Date等)時,就可以通過介面中的compareTo對物件進行排序。此種排序方式為自然順序。 2.使用比較器介面Comparator實現。有時我們可能需要自定義元素排序的順序,或者說物件不是Comparable的例項,就可以通過比較器中的compare(object e1, object e2)方法來實現自定義的排序。此種排序方式為比較器順序。
主要構造方法:
+TreeSet()  //建立一個無參例項
+TreeSet( Collection<? extends E> c) //從集合c中建立LinkedHashSet例項
+TreeSet(int size) //建立一個容量為size的例項

以下為實現比較器順序的一個案例(以學生資訊為例,可通過學號排序,也可通過成績排序):

import java.io.Serializable;
import java.util.Comparator;

public class StudentComparator implements Comparator<Student>, Serializable{ //Java所有集合中都實現了序列化,為了能成功排序,必須實現該介面

	@Override
	public int compare(Student o1, Student o2) {
		if (o1.getGrade() > o2.getGrade())
			return 1;
		else if (o1.getGrade() == o2.getGrade())
			return 0;
		else 
			return -1;	
	}
	
}
<pre name="code" class="java">import java.util.Iterator;
import java.util.TreeSet;

public class TestTreeSet {
	public static void main(String[] args) {
		TreeSet<Student> set = new TreeSet<>(new StudentComparator());
		set.add(new Student(1, 80));
		set.add(new Student(2, 50));
		set.add(new Student(3, 78));

		Iterator<Student> iterator = set.iterator();  //使用迭代器遍歷元素
		while (iterator.hasNext()) {
			Student student = (Student) iterator.next();
			System.out.println(student.getId() + " " + student.getGrade());
		}
	}
}

/*以下為輸出結果
2 50
3 78
1 80*/

2、線性表List

在程式開發中,往往我們需要向集合中新增相同的元素,線性表實現了新增重複元素的功能,此外,線性表也允許通過下標訪問集合中指定位置的元素。線性表是一個有序允許重複的集合。

List介面中方法有:

+add(int index) //指定下標新增元素
+addAll(int index, Collection<? extends E> c) //指定下標處新增c中所有元素
+get(int index) //返回指定下標元素
+lastIndexOf(Object o) //返回相同元素的下標
+listIterator() //返回遍歷列表的迭代器
+listIterator(int startIndex) //返回從startIndex開始的所有元素的迭代器
+remove(int index) //刪除指定下標的元素
+set(int index, E element) //設定指定下標的元素
+subList(int fromIndex, int toIndex) //返回從fromIndex到toIndex元素子列表 
List介面中有兩個具體實現類:陣列線性表ArrayList和連結串列LinkedList。

ArrayList

ArrayList使用陣列來儲存元素,這個陣列是動態建立的,當插入的元素超過陣列的長度時,就會建立更大的陣列,並把當前陣列的元素複製到新的陣列當中。因此相對於陣列來說ArrayList更具有靈活性。

構造方法:

+ArrayList() //建立一個空列表
+ArrayList(Collection<? extends E> c) //從集合c中建立例項
+ArrayList(int size) //建立一個大小為size的空列表
此外,ArrayList中有trimToSize()方法可以將ArrayList的容量縮小到當前列表大小。

在Java2之前引入了向量類Vector,其使用方式與ArrayList類似,但Vector實現了現成同步,以避免多執行緒訪問資料時引起陣列損壞。關於執行緒同步,會在後續文章中提及。

LinkedList

LinkedList是一個雙向連結串列,除了實現List介面的方法還實現了新增和刪除表頭表尾元素的方法。 構造方法及其他方法:
+LinkedList() //建立一個空連結串列
+LinkedList(int size) //建立一個容量為size的空連結串列
+addFirst(E e) //新增表頭元素
+addLast(E e) //新增表尾元素
+getFirst() //返回表頭元素
+getLast() //返回表尾元素
+removeFirst() //移除表頭元素
+removeLast() //移除表尾元素

3、佇列Queue

Queue,佇列,是一種先進先出的資料結構。新增的元素會插在佇列的末尾。在優先佇列中,優先順序高的元素會首先出隊。Queue介面中主要有以下方法:
+offer(E e) //新增元素
+poll() // 返回並刪除隊頭元素,否則返回null
+remove() //返回並刪除隊頭元素,否則丟擲異常
+peek() // 返回隊頭元素,否則返回null
+element() //返回隊頭元素,否則丟擲異常

Queue中有兩個具體實現類:連結串列LinkedList和優先佇列PriorityQueue。

LinkedList

在上述的List介面中也提到過LinkedList,它同時擴充套件自List介面和Deque介面。雙向佇列Deque介面擴充套件自Queue介面,支援在佇列的兩端在兩端插入或刪除資料。具體方法可參考上述內容。

PriorityQueue

此類實現了優先佇列,在預設情況下,該佇列的初始容量為11。其例項所儲存的元素預設以自然順序排列,因此自然順序下最小的元素會優先出隊。佇列中可能出現對個優先順序相同的元素,那麼擁有相同優先順序的元素會有其中任意一個優先出隊。在講述TreeSet時提到過使用Comparator介面來實現比較器順序,在優先佇列中依然可行。

構造方法:

+PriorityQueue() //建立一個預設的優先佇列
+PriorityQueue(int size) //建立一個容量為size的優先佇列
+PriorityQueue(Collection<? extends E> c) //從集合c中建立優先佇列
+PriorityQueue(int size, Comparator<? super E>) //建立一個容量為size且擁有比較器順序的優先佇列

二、Map介面

Java集合框架中支援三種類型的圖:雜湊圖HashMap,鏈式雜湊圖LinkedHashMap和樹形圖TreeMap。其層級關係與規則集Set類似。 Map----          |          |----SortMap----TreeMap          |          |----HashMap----LinkedHashMap 圖是按照鍵值儲存資料的容器,這一點與List有相似之處,鍵值類似於List中的下標,不同的是Map的鍵值可以是任意物件,同時鍵值不能重複。圖中每個鍵值對應著一個值,鍵與值一起儲存在圖中。

Map介面中有如下方法:

+clear(); //刪除圖中所有條目
+containsKey(Object key) //圖中如果包含指定鍵值返回true
+containsValue(Object value) //圖中如果包含指定值返回true
+get(Object key) //獲得指定鍵值對應的值
+entrySet() //返回包含圖中條目的規則集
+isEmpty() //判斷是否空
+keySet() //返回圖中包含鍵值的一個規則集
+put(Object key, Object value) //新增鍵值對
+putAll( ) //將指定例項中的鍵值對新增到當前例項中
+remove(Object key) //刪除指定鍵值對應的值
+size() //鍵值對個數
+values() //返回圖中包含的集合

HashMap

與HashSet相類似,HashMap例項儲存的鍵值對是無序的,不會根據插入的順序將鍵值對進行排序,並且儲存順序是隨機的。

LinkedHashMap

該類擴充套件自HashMap,實現了插入的鍵值對的排序。其排序方式有兩種,一種根據插入順序排序(插入順序),一種根據最後一次被訪問的順序排序(訪問順序)。通過以下構造方法說明:

+LinkedHashMap(int size, float loadFactor, boolean Order) //建立一個容量為size的圖,客座率為loadFactor(即儲存的鍵值對超過該值,會自動增加圖的容量,一般為0.75f),true代表訪問順序,false代表插入順序

TreeMap

與TreeSet類似,可通過兩種方式來實現對圖中的鍵值進行排序。

上述三種具體類都可以通過構造方法從其他圖中進行建立。以下為一些

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

public class TestMap {
	public static void main(String[] args) {
		Map<String, Integer> hashmap = new HashMap<>();
		hashmap.put("s1", 10);
		hashmap.put("s2", 6);
		hashmap.put("s3", 7);
		System.out.println("HashMap:");
		System.out.println(hashmap);
		
		Map<String, Integer> linkedHashMap = new LinkedHashMap<>(3, 0.75f, true);
		linkedHashMap.put("s1", 10);
		linkedHashMap.put("s2", 6);
		linkedHashMap.put("s3", 7);
		System.out.println("LinkedHashMap:");
		System.out.println(linkedHashMap);
		
		Map<String, Integer> treeMap = new TreeMap<>();
		treeMap.put("s1", 10);
		treeMap.put("s2", 6);
		treeMap.put("s3", 7);
		System.out.println("TreeMap:");
		System.out.println(treeMap);
	}
	
}
/*輸出結果
HashMap:
{s3=7, s1=10, s2=6}
LinkedHashMap:
{s1=10, s2=6, s3=7}
TreeMap:
{s1=10, s2=6, s3=7}*/

總結

HashSet與HashMap都不能儲存相同的元素。其判斷是否存在相同元素通過hashcode()和equals()兩個方法來實現,當插入一個新元素或鍵值時,會先判斷集合或圖中是否存在hashcode()值相同的元素,虛擬碼如下:
if (兩個元素hashcode值相等) {
    if (兩個元素相等) 
        return true; //不新增
    else 
        return false; //新增
}

由此可知,hashcode相等的兩個元素未必相同,但兩個相同的元素hashcode必定相同。

在Set介面的例項中如果不需要維護元素插入的順序,則使用HashSet,因為HashSet更高效率。Map介面中的HashMap也是如此。

在List介面中,ArrayList在尾部提取和插入元素比較高效,LinkedList在任意位置刪除和插入元素較高效。

如果在應用程式中不需要新增重複的元素,那麼規則集會是最高效的集合。