由上面的圖可以非常清楚的看到,Java中的容器的繼承結構
在頂層有很多接口,這些接口聲明了很多的基本的抽象方法,之后的許多類按照不同的方式實現這些接口,
同時可能在增加一些自己的方法,從而形成了不同功能的容器,比如:ArrayList類與LinkedList類都繼承了List接口,
但是他們在實現List的接口時,方法體並不一樣,這樣一來就形成了不一樣的容器,這些容器都位於java.util包中
接下來我們將一一介紹這些容器的功能,當然我們不可能將容器的所有的知識全部不漏的講解,我們只是將一些
思想講解一下,使我們對容器有一個整體的認識,至於一些具體的方法如何使用,需要在用時查詢官方文檔
首先我們先對List、Set、Queue、Map接口的功能做一個介紹:
①List類型的容器實際上就相當於一個長度可變的
“對象數組”,和向量的概念是相同的
②Set類型的容器實際上相當於一個“對象集合”
這樣的容器中是不允許加入重復的元素的,並且
對於其中的元素並沒有一種排序的概念,和集合
是一個概念
③Queue類型的容器實際上相當於一個“對象隊列”
④Map類型的容器中的每個元素包含一對兒鍵對象和
值對象,即在關鍵字對象和值對象之間產生一種映射關系,通過鍵對象類查找值對象
接下來,我們介紹逐一介紹這些具體的容器都有什么功能
實例:
Collection<String> c = new ArrayList<String>
(Array.asList("A","B","C"));
一、Collection 接口
這個接口只一個抽象度非常高的接口,這其中聲明了那些實現了他的容器類都具有的特性方法,也就是諸如:
ArrayList類、LinkedList類、HashSet類等其中的繼承自Collection接口的方法的實現方式都是一樣的,
比如:boolean add(E e) 這個方法在所有的這些類中都是將一個元素加入容器當中;
即Collection中的抽象方法都是從“一堆元素”的概念上抽象出來的Collection接口的常用抽象方法有:
① public Boolean add(E e)
向容器當中添加一個元素
②boolean addAll(Collection<? extends E> c)
向當前的容器中加入指定的Collection類型的容器的所有元素
③void clear()
移除容器當中的所有的元素
④boolean contains(Object obj)
判定當前的容器中是否有指定的元素,我們想肯定調用了equals()方法了
⑤boolean containsAll(Collection<?> c)
判定當前容器中是否包含了,指定的容器中的所有的元素
⑥boolean isEmpty()
判定當前的容器是否為空
⑦Iterator<E> iterator()
返回當前容器對應的迭代器
⑧boolean remove(Object obj)
如果容器當中含有該元素,則將之刪除
⑨boolean retainAll(Collection<?> C)
將當前的容器中的元素和指定的容器元素取交集存入當前容器中
10.int size ()
返回容器的元素數目
11.Object[] toArray()
根據當前容器中的元素創建一個對象數組
二、實用類Collections
在上面的圖中的右下角有兩個實用類:Collections類
和Arrays類,Arrays類中的靜態方法主要是操作數組的,而Collection類中也有很多靜態的方法是用來操作List、Set、Queue、Map類型的容器的,
這兩個實用類都位於java.util包中
在Arrays類中也有一個操縱容器的方法
static List<T> |
asList(T... a) |
返回有可變參數列表創建的List類型的容器對象,並返回容器的引用,如:
String [] strArray = {“123”,”wr”,”fsdf”};
List <String> ls = Array.asList(strArray);
在Collection類中的靜態方法,能夠實現容器中元素的替換、刪除、排序、整個復制等等,具體的就不在多講
三、List(列表)
像數組一樣,List也能建立數字索引與對象的關聯,表達的是數據結構中的線性表的概念,List接口的常用實現類是:ArrayList和LinkedList
①ArrayList類
就像名子顯示的那樣,ArrayList是線性表中的數組,也就是說這個類中的數據域實際上就是一個對象數組,
因此,他有數組的一切的特性,還有同時封裝在一起的方法
因此對於查找來說,速度是非常的快的,但是刪除與插入操作就相對較慢,而且ArrayList是線程不安全的
②LinkedList類
就像名字顯示的那樣,LinkedList是線性表當中的鏈表,也就是說這個類中的數據域實際上就是一個對象鏈表,
還有同時封裝在一起的方法,值的注意的是內部的這個鏈表是一個雙向的循環鏈表,
也就是說,插入語刪除操作是非常快的,但是查找較慢
另外需要非常注意的是,LinkedList類還繼承了Queue接口,
LinkedList類中單獨還有addFirst()、addLast()、getFirst()、getLast()、removeFirst()、removeLast()等方法,
這就是的LinkedList類的對象能夠用作隊列和棧來使用,同樣,LinkedList是線程不安全的
四、Set(集合)
1、Set 中不接受任何重復的元素,如果試圖將重復的對象加到同一個Set對象當中的話,是不能加入的;所謂的相同的對象,
是根據equals(參數)方法來判定的,這就要涉及到該方法的重寫問題了,這在之前已經討論過了
2、一般Set最常用於判定一個指定元素的歸屬性問題,即判定一個元素是否已經在在當前的集合當中了!可見查找
是Set型容器的最常用的、最重要的方法了;Set與Collection具有完全一樣的接口,實際上Set就是Collection,只是應用的行為不同罷了!
3、雖然Set有HashSet、TreeSet、LinkedSet三種實現類,但是在《thinking in java》 中明確指出,
對於查找而言HashSet類失效率最高的一個類,所以在沒有特殊的要求時,使用HashSet是最好的選擇
<HashSet類>
1、這個類的內部有一個HasMap實例對象作為其成員域,
這說明在HashSet類的實例對象中的元素都是按照哈希表的形式存儲在HashMap對象中了,這也就說明了,HashSet類的查找效率是非常高的
2、通過add(參數)向HashSet對象當中,添加元素時,首先會調用hasCode()方法,計算出該元素的存放位置,
比較該位置的元素是否與要添加的元素相同,所以又要調用equals()方法;同時在查找時也要調用hashCode方法;
這就要求我們將equals()方法和hashCode方法同時重寫,保證兩個方法相匹配,即保證:
當兩個元素對象“相等時”,那么兩個元素對象應該生成相同的散列碼
這就用到了另一份文檔“Java中的hashCode()方法的深入剖析.doc”(其中那個例子非常有代表性)
我們在獲得這個容器中的每個元素時,可以使用簡化的for語句實現,這是允許的,可以查閱一下簡化的for語句的使用前提
五、Queue(隊列)
也就是說,Queue類型的容器專門用來用作容器來使用的
具有隊列的特性
①利用LinkedList類型的容器實現普通的隊列、雙向隊列、棧等數據結構(LinkedList類同時實現了Queue接口),
我們可以通過將LinkedList的對象向上轉型到Queue,這樣一來的話就只能使用繼承自Queue接口的專門用於隊列的方法,
使LinkedList對象看起來像一個完完全全的隊列容器;對於雙向隊列,java中有一個專門用於實現雙端隊列的接口Deque,位於java.uitl包中,
而且LinkedList類也實現了該接口,所以同樣可以將LinkedList對象向上轉型形成一個單純的雙端隊列
②PriorityQueue(優先級隊列)
所謂的優先級隊列就是在該隊列的內部有一個堆的數據結構,實現了每次出對的元素,都是優先級最高的元素;
但是程序是如何知道每個元素的優先級的呢,這里有兩種方式:自然方式和人為指定方式
所謂的自然方式是指,如果容器中的元素對象是String、Integer、或者是Character類型的內嵌數據類型的話,
那么這種優先級是程序已經自行規定好的了,即“較小”的擁有較高的優先級;但是對於實際問題中的很多的元素都不是內嵌的數據類型,
那么就要人為的指定優先級的設定規則,解決這一問題的方法就是,使元素類實現Comparable
接口,這個接口中只有一個方法
public int compareTo(Object obj)
只要在元素類中將該方法實現的話,那么程序就會自行的根據這個方法來設定優先級,如:
注意:對於PriorityQueue來說,他的遍歷器不能保證按照優先級次序便利所有的元素
六、Map(映射)
Map(映射)是一種將鍵對象和值對象進行映射的集合,需要注意的是如果值對象是Map類型的話那么,就形成了多級映射;
在實現該接口的類中效率最高的一個就是HashMap類,一般在沒有特殊的要求的情況下最好及使用這個類型的容器,
這個容器的用法和前面講過的Hashtable容器非常相近,可以參照該部分的內容進行理解,
其中最終要的一點是對於hashCode()和euqals()方法的重寫;
另外需要注意的一點是:如果在哈希表的應用中,迭代性能比查找性能更加站上風,那么在設定初始容量時,
就應該盡量的不讓初始容量太高,而同時讓裝填因子設定的得盡量的高;如果是說實際使用哈希表的時候,
有非常多的鍵值對要加入到該容器中的話,使用足夠大的初始容量創建該容器,將使得映射關系更加有效的存貯,
因為如果初始容量設定的過於小的話,那么容器就會按照規則不斷的進行擴充,那么就會耗費非常多的時間