1. 程式人生 > >Java 常用的三個集合類

Java 常用的三個集合類

講集合collection之前,我們先分清三個概念:

colection 集合,用來表示任何一種資料結構
Collection 集合介面,指的是 java.util.Collection介面,是 Set、List 和 Queue 介面的超類介面
Collections 集合工具類,指的是 java.util.Collections 類。

我們這裡說的集合指的是小寫的collection,集合有4種基本形式,其中前三種的父介面是Collection。

List 關注事物的索引列表
Set 關注事物的唯一性
Queue 關注事物被處理時的順序
Map 關注事物的對映和鍵值的唯一性


一、Collection 介面

Collection介面是 Set 、List 和 Queue 介面的父介面,提供了多數集合常用的方法宣告,包括 add()、remove()、contains() 、size() 、iterator() 等。

add(E e) 將指定物件新增到集合中
remove(Object o) 將指定的物件從集合中移除,移除成功返回true,不成功返回false
contains(Object o) 檢視該集合中是否包含指定的物件,包含返回true,不包含返回flase
size() 返回集合中存放的物件的個數。返回值為int
clear() 移除該集合中的所有物件,清空該集合。
iterator() 返回一個包含所有物件的iterator物件,用來迴圈遍歷
toArray() 返回一個包含所有物件的陣列,型別是Object
toArray(T[] t) 返回一個包含所有物件的指定型別的陣列


我們在這裡只舉一個把集合轉成陣列的例子,因為Collection本身是個介面所以,我們用它的實現類ArrayList做這個例子:
Java程式碼  收藏程式碼
  1. import java.util.ArrayList;  
  2. import java.util.Collection;  
  3. publicclass CollectionTest {  
  4.     publicstaticvoid main(String[] args) {  
  5.         String a = "a",b="b",c="c";  
  6.         Collection list = new ArrayList();  
  7.         list.add(a);  
  8.         list.add(b);  
  9.         list.add(c);  
  10.         String[] array =  list.toArray(new
     String[1]);  
  11.         for(String s : array){  
  12.             System.out.println(s);  
  13.         }  
  14.     }  
  15. }  

二、幾個比較重要的介面和類簡介

1、List介面


List 關心的是索引,與其他集合相比,List特有的就是和索引相關的一些方法:get(int index) 、 add(int index,Object o) 、 indexOf(Object o) 。

ArrayList 可以將它理解成一個可增長的陣列,它提供快速迭代和快速隨機訪問的能力。

LinkedList 中的元素之間是雙鏈接的,當需要快速插入和刪除時LinkedList成為List中的不二選擇。

Vector 是ArrayList的執行緒安全版本,效能比ArrayList要低,現在已經很少使用

2、Set介面

Set關心唯一性,它不允許重複。

HashSet 當不希望集合中有重複值,並且不關心元素之間的順序時可以使用此類。

LinkedHashset 當不希望集合中有重複值,並且希望按照元素的插入順序進行迭代遍歷時可採用此類。

TreeSet 當不希望集合中有重複值,並且希望按照元素的自然順序進行排序時可以採用此類。(自然順序意思是某種和插入順序無關,而是和元素本身的內容和特質有關的排序方式,譬如“abc”排在“abd”前面。)

3、Queue介面

Queue用於儲存將要執行的任務列表。

LinkedList 同樣實現了Queue介面,可以實現先進先出的佇列。

PriorityQueue 用來建立自然排序的優先順序佇列。番外篇中有個例子http://android.yaohuiji.com/archives/3454你可以看一下。

4、Map介面

Map關心的是唯一的識別符號。他將唯一的鍵對映到某個元素。當然鍵和值都是物件。

HashMap 當需要鍵值對錶示,又不關心順序時可採用HashMap。

Hashtable 注意Hashtable中的t是小寫的,它是HashMap的執行緒安全版本,現在已經很少使用。

LinkedHashMap 當需要鍵值對,並且關心插入順序時可採用它。

TreeMap 當需要鍵值對,並關心元素的自然排序時可採用它。

三、ArrayList的使用

ArrayList是一個可變長的陣列實現,讀取效率很高,是最常用的集合型別。

1、ArrayList的建立

在Java5版本之前我們使用:

1 Java程式碼  收藏程式碼
  1. List list = new ArrayList();  

在Java5版本之後,我們使用帶泛型的寫法:

1 Java程式碼  收藏程式碼
  1. List<String> list = new ArrayList<String>();  

上面的程式碼定義了一個只允許儲存字串的列表,尖括號括住的型別就是引數型別,也成泛型。帶泛型的寫法給了我們一個型別安全的集合。關於泛型的知識可以參見這裡。

2、ArrayList的使用:
       Java程式碼  收藏程式碼
  1. List<String> list = new ArrayList<String>();  
  2. list.add("nihao!");  
  3. list.add("hi!");  
  4. list.add("konikiwa!");  
  5. list.add("hola");  
  6. list.add("Bonjour");  
  7. System.out.println(list.size());  
  8. System.out.println(list.contains(21));  
  9. System.out.println(list.remove("hi!"));  
  10. System.out.println(list.size());  

關於List介面中的方法和ArrayList中的方法,大家可以看看JDK中的幫助。

3、基本資料型別的的自動裝箱:

我們知道集合中存放的是物件,而不能是基本資料型別,在Java5之後可以使用自動裝箱功能,更方便的匯入基本資料型別。

Java程式碼  收藏程式碼
  1. List<Integer> list = new ArrayList<Integer>();  
  2.     list.add(new Integer(42));  
  3.     list.add(43);  

4、ArrayList的排序:

ArrayList本身不具備排序能力,但是我們可以使用Collections類的sort方法使其排序。我們看一個例子:

Java程式碼  收藏程式碼
  1. import java.util.ArrayList;  
  2.     import java.util.Collections;  
  3.     import java.util.List;  
  4.     publicclass Test {  
  5.         publicstaticvoid main(String[] args) {  
  6.             List<String> list = new ArrayList<String>();  
  7.             list.add("nihao!");  
  8.             list.add("hi!");  
  9.             list.add("konikiwa!");  
  10.             list.add("hola");  
  11.             list.add("Bonjour");  
  12.             System.out.println("排序前:"+ list);  
  13.             Collections.sort(list);  
  14.             System.out.println("排序後:"+ list);  
  15.         }  
  16.     }   

編譯並執行程式檢視結果:

排序前:[nihao!, hi!, konikiwa!, hola, Bonjour]

排序後:[Bonjour, hi!, hola, konikiwa!, nihao!]

5、陣列和List之間的轉換

從陣列轉換成list,可以使用Arrays類的asList()方法:
Java程式碼  收藏程式碼
  1. import java.util.ArrayList;  
  2. import java.util.Collections;  
  3. import java.util.List;  
  4. publicclass Test {  
  5.     publicstaticvoid main(String[] args) {  
  6.             String[] sa = {"one","two","three","four"};  
  7.             List list = Arrays.asList(sa);  
  8.             System.out.println("list:"+list);  
  9.             System.out.println("list.size()="+list.size());  
  10.     }  
  11. }  

6、Iterator和for-each

在for-each出現之前,我們想遍歷ArrayList中的每個元素我們會使用Iterator介面:

Java程式碼  收藏程式碼
  1. import java.util.Arrays;  
  2.     import java.util.Iterator;  
  3.     import java.util.List;  
  4.     publicclass Test {  
  5.         publicstaticvoid main(String[] args) {  
  6.             // Arrays類為我們提供了一種list的便捷建立方式
  7.             List<String> list = Arrays.asList("one""two""three""four");  
  8.             // 轉換成Iterator例項
  9.             Iterator<String> it = list.iterator();  
  10.             //遍歷
  11.             while (it.hasNext()) {  
  12.                 System.out.println(it.next());  
  13.             }  
  14.         }      
  15.     }  

在for-each出現之後,遍歷變得簡單一些:

Java程式碼  收藏程式碼
  1. import java.util.Arrays;  
  2. import java.util.Iterator;  
  3. import java.util.List;  
  4. publicclass Test {  
  5.     publicstaticvoid main(String[] args) {  
  6.         // Arrays類為我們提供了一種list的便捷建立方式
  7.         List<String> list = Arrays.asList("one""two""three""four");  
  8.         for (String s : list) {  
  9.             System.out.println(s);  
  10.         }      
  11.            }       
  12. }  

一、Map介面

Map介面的常用方法如下表所示:

put(K key, V value) 向集合中新增指定的鍵值對
putAll(Map <? extends K,? extends V> t) 把一個Map中的所有鍵值對新增到該集合
containsKey(Object key) 如果包含該鍵,則返回true
containsValue(Object value) 如果包含該值,則返回true
get(Object key) 根據鍵,返回相應的值物件
keySet() 將該集合中的所有鍵以Set集合形式返回
values() 將該集合中所有的值以Collection形式返回
remove(Object key) 如果存在指定的鍵,則移除該鍵值對,返回鍵所對應的值,如果不存在則返回null
clear() 移除Map中的所有鍵值對,或者說就是清空集合
isEmpty() 檢視Map中是否存在鍵值對
size() 檢視集合中包含鍵值對的個數,返回int型別

因為Map中的鍵必須是唯一的,所以雖然鍵可以是null,只能由一個鍵是null,而Map中的值可沒有這種限制,值為null的情況經常出現,因此get(Object key)方法返回null,有兩種情況一種是確實不存在該鍵值對,二是該鍵對應的值物件為null。為了確保某Map中確實有某個鍵,應該使用的方法是 containsKey(Object key) 。

二、HashMap

HashMap是最常用的Map集合,它的鍵值對在儲存時要根據鍵的雜湊碼來確定值放在哪裡。

1、HashMap的基本使用:

Java程式碼  收藏程式碼
  1. import java.util.Collection;  
  2.     import java.util.HashMap;  
  3.     import java.util.Map;  
  4.     import java.util.Set;  
  5.     publicclass Test {    
  6.         publicstaticvoid main(String[] args) {  
  7.             Map<Integer,String> map = new HashMap<Integer,String>();  
  8.             map.put(1"白菜");  
  9.             map.put(2"蘿蔔");  
  10.             map.put(3"茄子");  
  11.             map.put(4null);  
  12.             map.put(nullnull);  
  13.             map.put(nullnull);  
  14.             System.out.println("map.size()="+map.size());  
  15.             System.out.println("map.containsKey(1)="+map.containsKey(2));                   System.out.println("map.containsKey(null)="+map.containsKey(null));  
  16.             System.out.println("map.get(null)="+map.get(null));  
  17.             System.out.println("map.get(2)="+map.get(2));  
  18.             map.put(null"黃瓜");  
  19.             System.out.println("map.get(null)="+map.get(null));  
  20.             Set set = map.keySet();  
  21.             System.out.println("set="+set);  
  22.             Collection<String> c = map.values();  
  23.             System.out.println("Collection="+c);  
  24.         }  
  25.     }  

編譯並執行程式,檢視結果:
Java程式碼  收藏程式碼
  1. map.size()=5
  2. map.containsKey(1)=true
  3. map.containsKey(null)=true
  4. map.get(null)=null
  5. map.get(2)=蘿蔔  
  6. map.get(null)=黃瓜  
  7. set=[null1234]  
  8. Collection=[黃瓜, 白菜, 蘿蔔, 茄子, null]  

2、HashMap 中作為鍵的物件必須重寫Object的hashCode()方法和equals()方法

下面看一個我花了1個小時構思的例子,熟悉龍槍的朋友看起來會比較親切,設定了龍和龍的巢穴,然後把它們用Map集合對應起來,我們可以根據龍檢視它巢穴中的寶藏數量,例子只是為了說明hashCode這個知識點,所以未必有太強的故事性和合理性,湊合看吧:

Java程式碼  收藏程式碼
  1. import java.util.HashMap;  
  2.     import java.util.Map;  
  3.     publicclass Test {  
  4.         publicstaticvoid main(String[] args) {  
  5.             // 龍和它的巢穴對映表
  6.             Map<dragon , Nest> map = new HashMap<dragon , Nest>();  
  7.             // 在Map中放入四隻克萊恩大陸上的龍
  8.             map.put(new Dragon("銳刃"98), new Nest(98));  
  9.             map.put(new Dragon("明鏡"95), new Nest(95));  
  10.             map.put(new Dragon("碧雷"176), new Nest(176));  
  11.             map.put(new Dragon("瑪烈"255), new Nest(255));  
  12.             // 檢視寶藏
  13.             System.out.println("碧雷巢穴中有多少寶藏:" + map.get(new Dragon("碧雷"176)).getTreasure());  
  14.         }  
  15.     }  
  16.     // 龍
  17.     class Dragon {  
  18.         Dragon(String name, int level) {  
  19.             this.level = level;  
  20.             this.name = name;  
  21.         }  
  22.         // 龍的名字
  23.         private String name;  
  24.         // 龍的級別
  25.         privateint level;  
  26.         publicint getLevel() {  
  27.             return level;  
  28.         }  
  29.         publicvoid setLevel(int level) {  
  30.             this.level = level;  
  31.         }  
  32.         public String getName() {  
  33.             return name;  
  34.         }  
  35.         publicvoid setName(String name) {  
  36.             this.name = name;  
  37.         }  
  38.     }  
  39.     // 巢穴
  40.     class Nest {  
  41.         //我研究的龍之常數
  42.     finalint DRAGON_M = 4162;  
  43.         // 寶藏
  44.     privateint treasure;  
  45.         // 居住的龍的級別
  46.         privateint level;  
  47.         Nest(int level) {  
  48.             this.level = level;  
  49.             this.treasure = level * level * DRAGON_M;  
  50.         }  
  51.         int getTreasure() {  
  52.             return treasure;  
  53.         }  
  54.         publicint getLevel() {  
  55.             return level;  
  56.         }  
  57.         publicvoid setLevel(int level) {  
  58.             this.level = level;  
  59.             this.treasure = level * level * DRAGON_M;  
  60.         }  
  61.     }  

編譯並執行檢視結果:

Java程式碼  收藏程式碼
  1. Exception in thread "main" java.lang.NullPointerException  
  2.         at Test.main(Test.java:18)  

我們發現竟然報了錯誤,第18行出了空指標錯誤,也就是說get方法竟然沒有拿到預期的巢穴物件。

在這裡我們就要研究一下為什麼取不到了。我們這裡先解釋一下HashMap的工作方式。

假設現在有個6張中獎彩票的存根,放在5個桶裡(彩票首位只有1-5,首位是1的就放在一號桶,是2的就放在2號桶,依次類推),現在你拿了3張彩票來兌獎,一個號碼是113,一個號碼是213,一個號碼是313。那麼現在先兌第一張,取出一號桶裡的存根發現存根號碼和你的號碼不符,所以你第一張沒中獎。繼續兌第二張,二號桶裡就沒存根所以就直接放棄了,把三號桶裡的所有彩票存根都拿出來對應一番,最後發現有一個存根恰好是313,那麼恭喜你中獎了。

HashMap在確定一個鍵物件和另一個鍵物件是否是相同時用了同樣的方法,每個桶就是一個鍵物件的雜湊碼值,桶裡放的就是雜湊碼相同的彩票存根,如果雜湊碼不同,那麼肯定沒有相關元素存在,如果雜湊碼相同,那麼還要用鍵的equals()方法去比較是否相同,如果相同才認為是相同的鍵。簡單的說就是 hashCode()相同 && equals()==true 時才算兩者相同。

到了這裡我們應該明白了,在沒有重寫一個物件的hashcode()和equals()方法之前,它們執行的是Object中對應的方法。而Object的hashcode()是用物件在記憶體中存放的位置計算出來的,每個物件例項都不相同。Object的equals()的實現更簡單就是看兩個物件是否==,也就是兩個物件除非是同一個物件,否則根本不會相同。因此上面的例子雖然都是名字叫碧雷的龍,但是HashMap中卻無法認可它們是相同的。

因此我們只有重寫Key物件的hashCode()和equals()方法,才能避免這種情形出現,好在Eclipse可以幫我們自動生成一個類的hashCode()和equals(),我們把上面的例子加上這兩個方法再試試看:
       Java程式碼  收藏程式碼
  1. import java.util.HashMap;  
  2. import java.util.Map;  
  3. publicclass Test {  
  4.     publicstaticvoid main(String[] args) {  
  5.         // 龍和它的巢穴對映表
  6.         Map<dragon , Nest> map = new HashMap<dragon , Nest>();  
  7.         // 在Map中放入四隻克萊恩大陸上的龍
  8.         map.put(new Dragon("銳刃"98), new Nest(98));  
  9.         map.put(new Dragon("明鏡"95), new Nest(95));  
  10.         map.put(new Dragon("碧雷"176), new Nest(176));  
  11.         map.put(new Dragon("瑪烈"255), new Nest(255));  
  12.         // 檢視寶藏
  13.         System.out.println("碧雷巢穴中有多少寶藏:" + map.get(new Dragon("碧雷"176)).getTreasure());  
  14.     }  
  15. }  
  16. // 龍
  17. class Dragon {  
  18.     Dragon(String name, int level) {  
  19.         this.level = level;  
  20.         this.name = name;  
  21.     }  
  22.     // 龍的名字
  23.     private String name;  
  24.     // 龍的級別
  25.     privateint level;  
  26.     publicint getLevel() {  
  27.         return level;  
  28.     }  
  29.     publicvoid setLevel(int level) {  
  30.         this.level = level;  
  31.     }  
  32.     public String getName() {  
  33.         return name;  
  34.     }  
  35.     publicvoid setName(String name) {  
  36.         this.name = name;  
  37.            }       
  38.     @Override
  39.     publicint hashCode() {  
  40.         finalint PRIME = 31;  
  41.         int result = 1;  
  42.         result = PRIME * result + level;  
  43.         result = PRIME * result + ((name == null) ? 0 : name.hashCode());  
  44.         return result;  
  45.     }  
  46.     @Override
  47.     publicboolean equals(Object obj) {  
  48.         if (this == obj)  
  49.             returntrue;  
  50.         if (obj == null)  
  51.             returnfalse;  
  52.         if (getClass() != obj.getClass())  
  53.             returnfalse;  
  54.         final Dragon other = (Dragon) obj;  
  55.         if (level != other.level)  
  56.             returnfalse;  
  57.         if (name == null) {  
  58.             if (other.name != null)  
  59.                 returnfalse;  
  60.         } elseif (!name.equals(other.name))  
  61.             returnfalse;  
  62.         returntrue;  
  63.     }  
  64. }  
  65. // 巢穴
  66. class Nest {  
  67.     //我研究的龍之常數
  68.     finalint DRAGON_M = 4162;  
  69.     // 寶藏
  70.     privateint treasure;  
  71.     // 居住的龍的級別
  72.     privateint level;  
  73.     Nest(int level) {  
  74.         this.level = level;  
  75.         this.treasure = level * level * DRAGON_M;  
  76.     }  
  77.     int getTreasure() {  
  78.         return treasure;  
  79.     }  
  80.     publicint getLevel() {  
  81.         return level;  
  82.     }  
  83.     publicvoid setLevel(int level) {  
  84.         this.level = level;  
  85.         this.treasure = level * level * DRAGON_M;  
  86.     }  
  87. }  

編譯並執行檢視結果:

Java程式碼  收藏程式碼
  1. 碧雷巢穴中有多少寶藏:128922112