Java容器類基礎

Java容器類基礎

       Java實用類庫提供了一套相當完善的容器類,基本類型爲List,Set,Map,Queue。它們都有一些特性,例如Set對於每一個值都只保存一個對象,Map允許你將對象和對象關聯起來。此外,Java容器類都可以自動調節尺寸。因此,與數組不同,你可以放置任意數量的對象到容器中而不用擔心容器應該設置爲多大。

       Java容器類有4個接口,它們分別上面提到過的List,Set,Map,Queue;在理想情況下,你編寫的代碼大多數情況是在和這些接口打交道,並且你唯一需要指定所要使用精確類型的地方就是在創建的時候,例如你可以創建一個list:

List<Apple> list=new ArrayList<Apple>();

這裏使用接口的目的在於當你需要修改你的實現時,僅需要在創建的地方修改它即可,因此可以像下面這樣進行修改:

List<Apple> list=new LinkedList<Apple>();

因此,你應該創建一個具體的類的對象,然後將其轉換成對應的接口,然後再其餘的代碼中都使用這個接口。但這個方法並非總能奏效,因爲某些類具有額外的功能。例如LinkedList具有List接口中未包含的額外方法,因此如果你需要使用這些方法,你就不能將其轉換成更加通用的接口。

在這裏,我們將對Java容器類的基本知識及典型用法進行重點介紹。

1,基本概念

Java容器類庫的用途是保存對象,並將其劃分爲兩個不同的概念:

1)Collection:一個獨立元素的序列。list必須按照插入的順序保存元素,set不能有重複的元素,queue按照排隊規則來確定元素的順序。

2)Map:一組成對的“鍵值對”對象,允許你用鍵來查找值。ArrayList允許你用數字來查找值,從某種意義上來說,它將數字與對象關聯在一起。Map允許我們用一個對象查找另一個對象,又稱其爲關聯數組,應爲它將某些對象和另外一些對象關聯在了一起。

<span style="font-size:18px;">import java.util.*;

public class PrintingContainers{
	static Collection fill(Collection<String> collection){
		collection.add("rat");
		collection.add("cat");
		collection.add("dog");
		collection.add("dog");
		return collection;
	}
	static Map fill(Map<String,String> map){
		map.put("rat","123");
		map.put("cat","456");
		map.put("dog","789");
		map.put("dog","268");
		return map;
	}
	public static void main(String[] args){
		System.out.println(fill(new ArrayList<String>()));
		System.out.println(fill(new LinkedList<String>()));
		System.out.println(fill(new HashSet<String>()));
		System.out.println(fill(new TreeSet<String>()));
		System.out.println(fill(new LinkedHashSet<String>()));
		System.out.println(fill(new HashMap<String,String>()));
		System.out.println(fill(new TreeMap<String,String>()));
		System.out.println(fill(new LinkedHashMap<String,String>()));
	}
}
/*output:
[rat, cat, dog, dog]
[rat, cat, dog, dog]
[cat, dog, rat]
[cat, dog, rat]
[rat, cat, dog]
{cat=456, dog=268, rat=123}
{cat=456, dog=268, rat=123}
{rat=123, cat=456, dog=268}
*/
</span>

這裏展示了Java容器類庫中的兩種主要類型,它們的區別在於容器中每個“槽”保存元素的個數。查看輸出可以發現,默認的打印行爲(使用容器提供的toString方法)即可生成可讀性很好地結果。Collection打印出來的內容用方括號括住,每個元素由逗號分隔。Map則用大括號括住,鍵與值用等號聯繫。這些容器在這裏僅作爲展示之用,後面會對其進行更加詳細的介紹。

2,常用容器類介紹

1)Collection

下表列出了可以通過Collection執行的所有方法,它們也是Set和List可以執行的操作。

2)List

List接口在Collection基礎上添加了大量的方法,使得可以在List的中間插入和移除元素。

有兩種類型的List:

·基本的ArrayList,它擅長隨機訪問元素,但是在List中間插入和刪除元素較慢(底層實現:數組)。

·LinkedList,它通過較低的代價在List中間進行插入和刪除操作,提供了優化的順序訪問,但在隨機訪問方面相對較慢(底層實現:雙向鏈表)。

下面列出一些List中常用的方法:

contains():用來確定某個對象是否在列表中

remove():用來移除列表中的對象

indexOf():用來確定對象在列表中的索引編號

subList():用來創建一個列表片段

retainAll():用來進行“交集”操作

set():用來設置指定索引處對象

3)LinkedList

LinkedList一樣實現了基本的List接口,還添加了使其可以作爲棧,隊列以及雙端隊列的方法。

這些方法中有些彼此之間只是名稱上有些差異,或者只存在些許差異,以使得這些名字在特定的上下文中更加適用。例如:

getFirst()和element()完全一樣,它們都返回列表的第一個元素,而並不移除它;remove()和removeFirst()也是一樣的,它們移除並返回列表的頭;add()和addLast()都相同,它們都將元素插入列表的尾端;

4)Set

Set不保存重複的元素。Set最常用的就是測試歸屬性,你可以很容易的詢問某個對象是否在某個Set中。Set具有和Collection完全一樣的接口,因此沒有任何額外的功能,不像前面有兩個不同的List。實際上Set就是Collection,只是行爲不同(這是繼承和多態的典型應用);


<span style="font-size:18px;">import java.util.*;

public class SetOfOperations{
	public static void main(String[] args){
		Set<String> set1=new HashSet<String>();
		Collections.addAll(set1,"A B C D E F G H I J K L".split(" "));
		set1.add("M");
		System.out.println("H: "+set1.contains("H"));
		System.out.println("N: "+set1.contains("N"));
		Set<String> set2=new HashSet<String>();
		Collections.addAll(set2,"H I J K L".split(" "));
		System.out.println("set2 in set1: "+set1.containsAll(set2));
		set1.remove("H");
		System.out.println(set1);
		System.out.println("set2 in set1: "+set1.containsAll(set2));
		set1.removeAll(set2);
		System.out.println("set2 remove from set1: "+set1);
		Collections.addAll(set1,"X Y Z".split(" "));
		System.out.println("'X Y Z' add to set1: "+set1);
	}
}
/*
output:
H: true
N: false
set2 in set1: true
[D, E, F, G, A, B, C, L, M, I, J, K]
set2 in set1: false
set2 remove from set1: [D, E, F, G, A, B, C, M]
'X Y Z' add to set1: [D, E, F, G, A, B, C, M, Y, X, Z]
*/
</span>

5)Map


將對象映射到其他對象的能力是一種解決編程問題的殺手鐗;

例如下面的例子:

<span style="font-size:18px;">import java.util.*;

public class Statistics{
	public static void main(String[] args){
		Random rand=new Random(47);
		Map<Integer,Integer> m=new HashMap<Integer,Integer>();
		for(int i=0;i<10000;i++){
			int r=rand.nextInt(20);
			Integer feq=m.get(r);
			m.put(r,feq==null?1:feq+1);
		}
		System.out.println(m);
	}
}
/*
output:
{0=481, 1=502, 2=489, 3=508, 4=481, 5=503, 6=519, 7=471, 8=468, 9=549, 10=513, 11=531, 12=521, 13=506, 14=477, 15=497, 17=509, 16=533, 19=464, 18=478}
*/
</span>

6)Queue

隊列是一種典型的先進先出的容器,即從容器的一端放入事物,從另一端取出,並且事物放入容器的順序與取出的順序是相同的。隊列常被當作一種可靠的將對象從程序的某一區域傳送到另一區域的途徑,在併發編程中特別重要。

LinkedList提供了方法以支持隊列行爲,並且實現了Queue接口,因此LinkedList可以作爲Queue的一種實現。


<span style="font-size:18px;">import java.util.*;

public class QueueDemo{
	public static void printQueue(Queue queue){
		while(queue.peek()!=null){
			System.out.print(queue.remove()+" ");
		}
		System.out.println();
	}
	
	public static void main(String[] args){
		Queue<Integer> queue=new LinkedList<Integer>();
		Random rand=new Random(47);
		for(int i=0;i<10;i++)
			queue.offer(rand.nextInt(i+10));
		printQueue(queue);
		Queue<Character> qc=new LinkedList<Character>();
		for(char c:"HelloWorldJava".toCharArray())
			qc.offer(c);
		printQueue(qc);
	}
}
</span>

下面列出Queue的一些常用方法:

offer():將一個元素插入到隊尾

peek()/element():在不刪除的情況下返回對頭

poll()/remove():移除並返回對頭


3,迭代器

迭代器是一個對象,它的工作是遍歷選擇容器中的對象,它們只是使用容器,不知道或者不關心容器的類型,達到複用代碼的目的。此外,迭代器還被稱爲輕量級對象:創建它的代價小;

迭代器的用法:

1)使用iterator()要求容器返回一個Iterator();

2)使用next()獲取容器的中的下一個元素;

3)使用hasNext()檢查容器中是否還有元素;

4)使用remove()將迭代器新返回的元素刪除;


總結

Java容器簡圖,常用的容器用黑色粗線框表示,點線框表示接口,實線框表示具體的類;帶有空心箭頭的點線表示一個特定的類實現了接口,實心箭頭表示某個類可以生成箭頭所指向類的對象;



下面給出一些容器選擇的小建議:

1)如果要進行大量的隨機訪問,就使用ArrayList;如果經常需要從表中間插入和刪除元素,則應該使用LinkedList;

2)HashMap設計用來快速訪問;而TreeMap保持鍵始終處於排序狀態,所以沒有HashMap快。LinkedHashMap保持元素的插入順序,但也通過散列提供了快速訪問能力。

3)TreeSet不接受重複元素。HashSet提供最快的查詢速度,而TreeSet保持元素處於排序狀態,LinkedHashSet以插入順序保存元素。

4)程序中不應該使用過時的Vector,Stack,HashTable;



發佈了253 篇原創文章 · 獲贊 26 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章