Java中的Map List Set等集合類
Map List Set等集合類:
一、概述
在JAVA的util包中有兩個所有集合的父介面Collection和Map,它們的父子關係:
+Collection 這個介面extends自 --java.lang.Iterable介面
├+List(介面 代表有序,可重複的集合。列表)
│├ ArreyList (Class 陣列,隨機訪問,沒有同步,執行緒不安全)
│├ Vector (Class 陣列 同步 執行緒全)
│├ LinkedList (Class 連結串列 插入刪除 沒有同步 執行緒不安全)
│└ Stack (Class)
└+Set(介面 不能含重複的元素。僅接收一次並做內部排序,集)
│├ HashSet (Class)
│├ LinkedHashSet (Class)
│└ TreeSet (Class)
+Map(介面)
├ +Map(介面 對映集合)
│ ├ HashMap (Class 不同步,執行緒不安全。除了不同和允許使用null 鍵值之外,與Hashtable大致相同)
│ ├ Hashtable (Class 同步 ,執行緒安全 。不允許實施null 鍵值)
│ ├ +SortedMap 介面
│
│ ├ TreeMap (Class)
│ ├
WeakHashMap (Class)
集合框架圖:
以下對眾多介面和類的簡單說明:首先不能不先說一下陣列(Array)
1、效率高,但容量固定且無法動態改變。array還有一個缺點是,無法判斷其中實際存有多少元素,length只是告訴我們array的容量。
2、Java中有一個Arrays類,專門用來操作array。
arrays中擁有一組static函式,
equals():比較兩個array是否相等。array擁有相同元素個數,且所有對應元素兩兩相等。
fill():將值填入array中。
sort():用來對array進行排序。
binarySearch():在排好序的array中尋找元素。
System.arraycopy():array的複製。
2) JAVA集合可以儲存和運算元目不固定的一組資料。
3) 若程式時不知道究竟需要多少物件,需要在空間不足時自動擴增容量,則需要使用容器類庫,array不適用。
二 set map list的區別
都是集合介面
set --其中的值不允許重複,無序的資料結構
list --其中的值允許重複,因為其為有序的資料結構
map--成對的資料結構,健值必須具有唯一性(鍵不能同,否則值替換)
List 按物件進入的順序儲存物件,不做排序或編輯操作。
Set對每個物件只接受一次,並使用自己內部的排序方法(通常,你只關心某個元素是否屬於Set,而不關心它的順序--否則應該使用List)。
Map同樣對每個元素儲存一份,但這是基於"鍵"的,Map也有內建的排序,因而不關心元素新增的順序。如果新增元素的順序對你很重要,應該使用 LinkedHashSet或者LinkedHashMap.
Collection 是物件集合, Collection 有兩個子介面 List 和 SetList 可以通過下標 (1,2..) 來取得值,值可以重複
而 Set 只能通過遊標來取值,並且值是不能重複的
ArrayList , Vector , LinkedList 是 List 的實現類
ArrayList 是執行緒不安全的, Vector 是執行緒安全的,這兩個類底層都是由陣列實現的
LinkedList 是執行緒不安全的,底層是由連結串列實現的
Map 是鍵值對集合
HashTable 和 HashMap 是 Map 的實現類
HashTable 是執行緒安全的,不能儲存 null 值
HashMap 不是執行緒安全的,可以儲存 null 值
三、 Collections類和Collection介面
Collections是針對集合類的一個幫助類,他提供一系列靜態方法實現對各種集合的搜尋、排序、執行緒安全化等操作。
Collection是最基本的集合介面,一個Collection代表一組Object,即Collection的元素(Elements)。一些 Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的 類,Java SDK提供的類都是繼承自Collection的“子介面”如List和Set。
所有實現 Collection 介面的類都必須提供兩個標準的建構函式:無引數的建構函式用於建立一個空的 Collection ,有一個 Collection 引數的建構函式用於建立一個新的 Collection ,這個新的 Collection 與傳入的 Collection 有相同的元素。後一個建構函式允許使用者複製一個 Collection 。
集合類的遍歷:遍歷通用Collection:
如何遍歷 Collection 中的每一個元素?不論 Collection 的實際型別如何,它都支援一個 iterator() 的方法,該方法返回一個迭代子,使用該迭代子即可逐一訪問 Collection 中每一個元素。典型的用法如下:Iterator it = collection.iterator(); // 獲得一個迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一個元素
}
由 Collection 介面派生的兩個介面是 List 和 Set 。 List 按物件進入的順序儲存物件,不做排序或編輯操作。 Set 對每個物件只接受一次,並使用自己內部的排序方法 ( 通常,你只關心某個元素是否屬於 Set, 而不關心它的順序-- 否則應該使用 List) 。
四、 List介面,有序可重複的集合
實際上有兩種List: 一種是基本的ArrayList,其優點在於隨機訪問元素,另一種是更強大的LinkedList,它並不是為快速隨機訪問設計的,而是具有一套更通用的方法。List : 次序是List最重要的特點:它保證維護元素特定的順序。List為Collection添加了許多方法,使得能夠向List中間插入與移除元素(這隻推薦LinkedList使用。)一個List可以生成ListIterator,使用它可以從兩個方向遍歷List,也可以從List中間插入和移除元素。
1. ArrayList類
1) ArrayList實現了可變大小的陣列。它允許所有元素,包括null。ArrayList沒有同步。
2) size,isEmpty,get,set方法執行時間為常數。但是add方法開銷為分攤的常數,新增n個元素需要O(n)的時間。其他的方法執行時間為線性。
3) 每個ArrayList例項都有一個容量(Capacity),即用於儲存元素的陣列的大小。這個容量可隨著不斷新增新元素而自動增加,但是增長演算法 並沒有定義。當需要插入大量元素時,在插入前可以呼叫ensureCapacity方法來增加ArrayList的容量以提高插入效率。
4) 和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。
5) 由陣列實現的List。允許對元素進行快速隨機訪問,但是向List中間插入與移除元素的速度很慢。ListIterator只應該用來由後向前遍歷ArrayList,而不是用來插入和移除元素。因為那比LinkedList開銷要大很多。
2. Vector類
Vector非常類似ArrayList,但是Vector是同步的。由Vector建立的Iterator,雖然和ArrayList建立的Iterator是同一介面,但是,因為Vector是同步的,當一個Iterator被建立而且正在被使用,另一個執行緒改變了Vector的狀態(例如,新增或刪除了一些元素),這時呼叫Iterator的方法時將丟擲ConcurrentModificationException,因此必須捕獲該異常。
3. LinkedList類
LinkedList實現了List介面,允許null元素。此外LinkedList提供額外的get,remove,insert方法在 LinkedList的首部或尾部。如下列方法:addFirst(), addLast(), getFirst(), getLast(), removeFirst() 和 removeLast(), 這些方法 (沒有在任何介面或基類中定義過)。這些操作使LinkedList可被用作堆疊(stack),佇列(queue)或雙向佇列(deque)。
注意LinkedList沒有同步方法。如果多個執行緒同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在建立List時構造一個同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
4. Stack 類
Stack繼承自Vector,實現一個後進先出的堆疊。Stack提供5個額外的方法使得Vector得以被當作堆疊使用。基本的push和pop方法,還有peek方法得到棧頂的元素,empty方法測試堆疊是否為空,search方法檢測一個元素在堆疊中的位置。Stack剛建立後是空棧。
用法:
package Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class TestList {
public static void main(String dd[]) {
// new了一個儲存list
List l = new ArrayList();
// 因為Collection framework只能儲存物件所以new封裝類
l.add(new Integer(1));
l.add(new Integer(2));
l.add(new Integer(3));
l.add(new Integer(4));
Iterator it = l.iterator();
//使用 迭代器(Iterator):
// hasNext是取值取的是當前值.他的運算過程是判斷下個是否有值如果有繼續.
while (it.hasNext()) {
System.out.println("iterator:Element in list is : " + it.next());
}
//用for迴圈和get()方法:
for (int i = 0; i < l.size(); i++) {
System.out.println("for:Element in list is : " + l.get(i));
}
}
}
LinkedList
package Test;
import java.util.Iterator;
import java.util.LinkedList;
public class TestLinkedList {
public static void main(String arg[]) {
LinkedList ll = new LinkedList();// 宣告LinkedList並例項化
// 使用add()方法新增元素
ll.add("a");
ll.add("b");
ll.add("c");
// 使用Iterator迭代器遍歷出集合的元素並列印
Iterator it = ll.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println("------------------");
// 向連結串列頭和尾分別新增x和z
ll.addFirst("z");
ll.addLast("x");
// 遍歷檢視新增後的結果
for (Iterator i = ll.iterator(); i.hasNext();) {
System.out.println(i.next());
}
}
}
ArrayList和LinkedList的區別。
1.ArrayList是實現了基於動態陣列的資料結構,LinkedList基於連結串列的資料結構。
2.對於隨機訪問get和set,ArrayList覺得優於LinkedList,因為LinkedList要移動指標。
3.對於新增和刪除操作add和remove,LinedList比較佔優勢,因為ArrayList要移動資料。
如果熟悉資料結構的同學,就會一下明白,ArrayList就是線性表的順序表示,LinkedList就是線性表的連結串列表示。
五、 Set介面,代表無序,不可重複的集合
Set具有與Collection完全一樣的介面,因此沒有任何額外的功能,不像前面有兩個不同的List。實際上Set就是Collection,只是行為不同。(這是繼承與多型思想的典型應用:表現不同的行為。)Set不儲存重複的元素(至於如何判斷元素相同則較為負責)
Set : 存入Set的每個元素都必須是唯一的,因為Set不儲存重複元素。加入Set的元素必須定義equals()方法以確保物件的唯一性。Set與Collection有完全一樣的介面。Set介面不保證維護元素的次序。
1. HashSet
為快速查詢設計的Set。存入HashSet的物件必須定義hashCode()。
2. TreeSet
儲存次序的Set, 底層為樹結構。使用它可以從Set中提取有序的序列。
3. LinkedHashSet
具有HashSet的查詢速度,且內部使用連結串列維護元素的順序(插入的次序)。於是在使用迭代器遍歷Set時,結果會按元素插入的次序顯示。
用法:
Set set=new HashSet();
String s1=new String("hello");
String s2=s1;
String s3=new String("world");
set.add(s1);
set.add(s2);
set.add(s3);
System.out.println(set.size());//列印集合中物件的數目 為 2。
Set 的 add()方法是如何判斷物件是否已經存放在集合中?
boolean isExists=false;
Iterator iterator=set.iterator();
while(it.hasNext()) {
String oldStr=it.next();
if(newStr.equals(oldStr)){
isExists=true;
}
}
六、 Map介面:對映
Map沒有繼承Collection介面, Map 提供 key 到 value 的對映,你可以通過“鍵”查詢“值”。一個 Map 中不能包含相同的 key ,每個 key 只能對映一個 value 。 Map 介面提供3 種集合的檢視, Map 的內容可以被當作一組 key 集合,一組 value 集合,或者一組 key-value 對映。
方法 put(Object key, Object value) 新增一個“值” ( 想要得東西 ) 和與“值”相關聯的“鍵” (key) ( 使用它來查詢 ) 。方法get(Object key) 返回與給定“鍵”相關聯的“值”。可以用 containsKey() 和 containsValue() 測試 Map 中是否包含某個“鍵”或“值”。 標準的 Java 類庫中包含了幾種不同的 Map : HashMap, TreeMap, LinkedHashMap, WeakHashMap, IdentityHashMap 。它們都有同樣的基本介面 Map ,但是行為、效率、排序策略、儲存物件的生命週期和判定“鍵”等價的策略等各不相同。
Map 同樣對每個元素儲存一份,但這是基於 " 鍵"
的, Map 也有內建的排序,因而不關心元素新增的順序。如果新增元素的順序對你很重要,應該使用 LinkedHashSet 或者 LinkedHashMap.
執行效率是 Map 的一個大問題。看看 get() 要做哪些事,就會明白為什麼在 ArrayList 中搜索“鍵”是相當慢的。而這正是 HashMap 提高速度的地方。 HashMap 使用了特殊的值,稱為“雜湊碼” (hash code) ,來取代對鍵的緩慢搜尋。“雜湊碼”是“相對唯一”用以代表物件的int
值,它是通過將該物件的某些資訊進行轉換而生成的(在下面總結二:需要的注意的地方有更進一步探討)。所有 Java 物件都能產生雜湊碼,因為 hashCode() 是定義在基類 Object 中的方法 。 HashMap 就是使用物件的 hashCode() 進行快速查詢的。此方法能夠顯著提高效能。
1. Hashtable類
Hashtable繼承Map介面,實現一個key-value對映的雜湊表。任何非空(non-null)的物件都可作為key或者value。Hashtable是同步的。
新增資料使用 put(key, value) ,取出資料使用get(key)
,這兩個基本操作的時間開銷為常數。
Hashtable 通過初始化容量 (initial capacity) 和負載因子 (load factor) 兩個引數調整效能。通常預設的 load factor0.75 較好地實現了時間和空間的均衡。增大 load factor 可以節省空間但相應的查詢時間將增大,這會影響像get
和 put 這樣的操作。
使用 Hashtable 的簡單示例如下,將 1 ,2
,3 放到 Hashtable 中,他們的 key 分別是 ”one” , ”two” , ”three” :
Hashtable numbers =new
Hashtable();
numbers.put(“one”, new Integer(1));
numbers.put(“two”, new Integer(2));
numbers.put(“three”, new Integer(3));
要取出一個數,比如 2 ,用相應的 key :
Integer n = (Integer)numbers.get(“two”);
System.out.println(“two=
”+ n);
由於作為 key 的物件將通過計算其雜湊函式來確定與之對應的 value 的位置,因此任何作為 key 的物件都必須實現 hashCode 方法和 equals 方法。 hashCode 方法和 equals 方法繼承自根類 Object ,如果你用自定義的類當作 key 的話,要相當小心,按照雜湊函式的定義,如果兩個物件相同,即 obj1.equals(obj2)=true
,則它們的 hashCode 必須相同,但如果兩個物件不同,則它們的 hashCode 不一定不同,如果兩個不同物件的 hashCode 相同,這種現象稱為衝突,衝突會導致操作雜湊表的時間開銷增大,所以儘量定義好的 hashCode() 方法,能加快雜湊表的操作。
如果相同的物件有不同的 hashCode ,對雜湊表的操作會出現意想不到的結果(期待的 get 方法返回null
),要避免這種問題,只需要牢記一條:要同時複寫 equals 方法和 hashCode 方法,而不要只寫其中一個。
Hashtable 是同步的。
2. HashMap類
HashMap和Hashtable類似,也是基於hash散列表的實現。不同之處在於 HashMap是非同步的,並且允許null,即null value和null key。,但是將HashMap視為Collection時 (values()方法可返回Collection),其迭代子操作時間開銷和HashMap的容量成比例。因此,如果迭代操作的效能相當重要的話,不要 將HashMap的初始化容量設得過高,或者load factor過低。
LinkedHashMap 類:類似於 HashMap ,但是迭代遍歷它時,取得“鍵值對”的順序是其插入次序,或者是最近最少使用 (LRU) 的次序。只比 HashMap 慢一點。而在迭代訪問時發而更快,因為它使用連結串列維護內部次序。
3. WeakHashMap類 (弱鍵( weak key ))
WeakHashMap是一種改進的HashMap,它是為解決特殊問題設計的,它對key實行“弱引用”,如果一個key不再被外部所引用,那麼該key可以被GC回收。
4. TreeMap 類
基於紅黑樹資料結構的實現。檢視“鍵”或“鍵值對”時,它們會被排序 ( 次序由 Comparabel 或 Comparator 決定 ) 。 TreeMap 的特點在於,你得到的結果是經過排序的。 TreeMap 是唯一的帶有 subMap() 方法的 Map ,它可以返回一個子樹。
5. IdentifyHashMap 類
使用 == 代替 equals() 對“鍵”作比較的 hash map 。專為解決特殊問題而設計。
用法:
1 新增,刪除操作:
Object put(Object key, Object value): 向集合中加入元素
Object remove(Object key): 刪除與KEY相關的元素
void putAll(Map t): 將來自特定映像的所有元素新增給該映像
void clear(): 從映像中刪除所有對映
2 查詢操作:Object get(Object key): 獲得與關鍵字key相關的值
Map集合中的鍵物件不允許重複,也就說,任意兩個鍵物件通過equals()方法比較的結果都是false.
但是可以將任意多個鍵獨享對映到同一個值物件上。
Conllections : 集合實用類
Conllections提供了供JAVA集合實用的靜態方法
七、 如何選擇
1、容器類和Array的區別、擇取
1)容器類僅能持有物件引用(指向物件的指標),而不是將物件資訊copy一份至數列某位置。
2)一旦將物件置入容器內,便損失了該物件的型別資訊。
2、
1) 在各種Lists中,最好的做法是以ArrayList作為預設選擇。當插入、刪除頻繁時,使用LinkedList();
Vector總是比ArrayList慢,所以要儘量避免使用。
2) 在各種Sets中,HashSet通常優於HashTree(插入、查詢)。只有當需要產生一個經過排序的序列,才用TreeSet。
HashTree存在的唯一理由:能夠維護其內元素的排序狀態。
3) 在各種Maps中,HashMap用於快速查詢。
4) 當元素個數固定,用Array,因為Array效率是最高的。
結論:最常用的是ArrayList,HashSet,HashMap,Array。而且,我們也會發現一個規律,用TreeXXX都是排序的。
注意:
1、Collection沒有get()方法來取得某個元素。只能通過iterator()遍歷元素。
2、Set和Collection擁有一模一樣的介面。
3、List,可以通過get()方法來一次取出一個元素。使用數字來選擇一堆物件中的一個,get(0)...。(add/get)
4、一般使用ArrayList。用LinkedList構造堆疊stack、佇列queue。
5、Map用 put(k,v) / get(k),還可以使用containsKey()/containsValue()來檢查其中是否含有某個key/value。
HashMap會利用物件的hashCode來快速找到key。
* hashing
雜湊碼就是將物件的資訊經過一些轉變形成一個獨一無二的int值,這個值儲存在一個array中。
我們都知道所有儲存結構中,array查詢速度是最快的。所以,可以加速查詢。
發生碰撞時,讓array指向多個values。即,陣列每個位置上又生成一個槤表。
6、Map中元素,可以將key序列、value序列單獨抽取出來。
使用keySet()抽取key序列,將map中的所有keys生成一個Set。
使用values()抽取value序列,將map中的所有values生成一個Collection。
為什麼一個生成Set,一個生成Collection?那是因為,key總是獨一無二的,value允許重複。