Java 常用的三個集合類
阿新 • • 發佈:2019-02-09
講集合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() 等。
我們在這裡只舉一個把集合轉成陣列的例子,因為Collection本身是個介面所以,我們用它的實現類ArrayList做這個例子:
Java程式碼
二、幾個比較重要的介面和類簡介
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程式碼
在Java5版本之後,我們使用帶泛型的寫法:
1 Java程式碼
上面的程式碼定義了一個只允許儲存字串的列表,尖括號括住的型別就是引數型別,也成泛型。帶泛型的寫法給了我們一個型別安全的集合。關於泛型的知識可以參見這裡。
2、ArrayList的使用:
Java程式碼
關於List介面中的方法和ArrayList中的方法,大家可以看看JDK中的幫助。
3、基本資料型別的的自動裝箱:
我們知道集合中存放的是物件,而不能是基本資料型別,在Java5之後可以使用自動裝箱功能,更方便的匯入基本資料型別。
Java程式碼
4、ArrayList的排序:
ArrayList本身不具備排序能力,但是我們可以使用Collections類的sort方法使其排序。我們看一個例子:
Java程式碼
編譯並執行程式檢視結果:
排序前:[nihao!, hi!, konikiwa!, hola, Bonjour]
排序後:[Bonjour, hi!, hola, konikiwa!, nihao!]
5、陣列和List之間的轉換
從陣列轉換成list,可以使用Arrays類的asList()方法:
Java程式碼
6、Iterator和for-each
在for-each出現之前,我們想遍歷ArrayList中的每個元素我們會使用Iterator介面:
Java程式碼
在for-each出現之後,遍歷變得簡單一些:
Java程式碼
一、Map介面
Map介面的常用方法如下表所示:
因為Map中的鍵必須是唯一的,所以雖然鍵可以是null,只能由一個鍵是null,而Map中的值可沒有這種限制,值為null的情況經常出現,因此get(Object key)方法返回null,有兩種情況一種是確實不存在該鍵值對,二是該鍵對應的值物件為null。為了確保某Map中確實有某個鍵,應該使用的方法是 containsKey(Object key) 。
二、HashMap
HashMap是最常用的Map集合,它的鍵值對在儲存時要根據鍵的雜湊碼來確定值放在哪裡。
1、HashMap的基本使用:
Java程式碼
編譯並執行程式,檢視結果:
Java程式碼
2、HashMap 中作為鍵的物件必須重寫Object的hashCode()方法和equals()方法
下面看一個我花了1個小時構思的例子,熟悉龍槍的朋友看起來會比較親切,設定了龍和龍的巢穴,然後把它們用Map集合對應起來,我們可以根據龍檢視它巢穴中的寶藏數量,例子只是為了說明hashCode這個知識點,所以未必有太強的故事性和合理性,湊合看吧:
Java程式碼
編譯並執行檢視結果:
Java程式碼
我們發現竟然報了錯誤,第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程式碼
編譯並執行檢視結果:
Java程式碼
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程式碼
- import java.util.ArrayList;
- import java.util.Collection;
- publicclass CollectionTest {
- publicstaticvoid main(String[] args) {
- String a = "a",b="b",c="c";
- Collection list = new ArrayList();
- list.add(a);
- list.add(b);
- list.add(c);
- String[] array = list.toArray(new
- for(String s : array){
- System.out.println(s);
- }
- }
- }
二、幾個比較重要的介面和類簡介
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程式碼
- List list = new ArrayList();
在Java5版本之後,我們使用帶泛型的寫法:
1 Java程式碼
- List<String> list = new ArrayList<String>();
上面的程式碼定義了一個只允許儲存字串的列表,尖括號括住的型別就是引數型別,也成泛型。帶泛型的寫法給了我們一個型別安全的集合。關於泛型的知識可以參見這裡。
2、ArrayList的使用:
Java程式碼
- List<String> list = new ArrayList<String>();
- list.add("nihao!");
- list.add("hi!");
- list.add("konikiwa!");
- list.add("hola");
- list.add("Bonjour");
- System.out.println(list.size());
- System.out.println(list.contains(21));
- System.out.println(list.remove("hi!"));
- System.out.println(list.size());
關於List介面中的方法和ArrayList中的方法,大家可以看看JDK中的幫助。
3、基本資料型別的的自動裝箱:
我們知道集合中存放的是物件,而不能是基本資料型別,在Java5之後可以使用自動裝箱功能,更方便的匯入基本資料型別。
Java程式碼
- List<Integer> list = new ArrayList<Integer>();
- list.add(new Integer(42));
- list.add(43);
4、ArrayList的排序:
ArrayList本身不具備排序能力,但是我們可以使用Collections類的sort方法使其排序。我們看一個例子:
Java程式碼
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- publicclass Test {
- publicstaticvoid main(String[] args) {
- List<String> list = new ArrayList<String>();
- list.add("nihao!");
- list.add("hi!");
- list.add("konikiwa!");
- list.add("hola");
- list.add("Bonjour");
- System.out.println("排序前:"+ list);
- Collections.sort(list);
- System.out.println("排序後:"+ list);
- }
- }
編譯並執行程式檢視結果:
排序前:[nihao!, hi!, konikiwa!, hola, Bonjour]
排序後:[Bonjour, hi!, hola, konikiwa!, nihao!]
5、陣列和List之間的轉換
從陣列轉換成list,可以使用Arrays類的asList()方法:
Java程式碼
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- publicclass Test {
- publicstaticvoid main(String[] args) {
- String[] sa = {"one","two","three","four"};
- List list = Arrays.asList(sa);
- System.out.println("list:"+list);
- System.out.println("list.size()="+list.size());
- }
- }
6、Iterator和for-each
在for-each出現之前,我們想遍歷ArrayList中的每個元素我們會使用Iterator介面:
Java程式碼
- import java.util.Arrays;
- import java.util.Iterator;
- import java.util.List;
- publicclass Test {
- publicstaticvoid main(String[] args) {
- // Arrays類為我們提供了一種list的便捷建立方式
- List<String> list = Arrays.asList("one", "two", "three", "four");
- // 轉換成Iterator例項
- Iterator<String> it = list.iterator();
- //遍歷
- while (it.hasNext()) {
- System.out.println(it.next());
- }
- }
- }
在for-each出現之後,遍歷變得簡單一些:
Java程式碼
- import java.util.Arrays;
- import java.util.Iterator;
- import java.util.List;
- publicclass Test {
- publicstaticvoid main(String[] args) {
- // Arrays類為我們提供了一種list的便捷建立方式
- List<String> list = Arrays.asList("one", "two", "three", "four");
- for (String s : list) {
- System.out.println(s);
- }
- }
- }
一、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程式碼
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Set;
- publicclass Test {
- publicstaticvoid main(String[] args) {
- Map<Integer,String> map = new HashMap<Integer,String>();
- map.put(1, "白菜");
- map.put(2, "蘿蔔");
- map.put(3, "茄子");
- map.put(4, null);
- map.put(null, null);
- map.put(null, null);
- System.out.println("map.size()="+map.size());
- System.out.println("map.containsKey(1)="+map.containsKey(2)); System.out.println("map.containsKey(null)="+map.containsKey(null));
- System.out.println("map.get(null)="+map.get(null));
- System.out.println("map.get(2)="+map.get(2));
- map.put(null, "黃瓜");
- System.out.println("map.get(null)="+map.get(null));
- Set set = map.keySet();
- System.out.println("set="+set);
- Collection<String> c = map.values();
- System.out.println("Collection="+c);
- }
- }
編譯並執行程式,檢視結果:
Java程式碼
- map.size()=5
- map.containsKey(1)=true
- map.containsKey(null)=true
- map.get(null)=null
- map.get(2)=蘿蔔
- map.get(null)=黃瓜
- set=[null, 1, 2, 3, 4]
- Collection=[黃瓜, 白菜, 蘿蔔, 茄子, null]
2、HashMap 中作為鍵的物件必須重寫Object的hashCode()方法和equals()方法
下面看一個我花了1個小時構思的例子,熟悉龍槍的朋友看起來會比較親切,設定了龍和龍的巢穴,然後把它們用Map集合對應起來,我們可以根據龍檢視它巢穴中的寶藏數量,例子只是為了說明hashCode這個知識點,所以未必有太強的故事性和合理性,湊合看吧:
Java程式碼
- import java.util.HashMap;
- import java.util.Map;
- publicclass Test {
- publicstaticvoid main(String[] args) {
- // 龍和它的巢穴對映表
- Map<dragon , Nest> map = new HashMap<dragon , Nest>();
- // 在Map中放入四隻克萊恩大陸上的龍
- map.put(new Dragon("銳刃", 98), new Nest(98));
- map.put(new Dragon("明鏡", 95), new Nest(95));
- map.put(new Dragon("碧雷", 176), new Nest(176));
- map.put(new Dragon("瑪烈", 255), new Nest(255));
- // 檢視寶藏
- System.out.println("碧雷巢穴中有多少寶藏:" + map.get(new Dragon("碧雷", 176)).getTreasure());
- }
- }
- // 龍
- class Dragon {
- Dragon(String name, int level) {
- this.level = level;
- this.name = name;
- }
- // 龍的名字
- private String name;
- // 龍的級別
- privateint level;
- publicint getLevel() {
- return level;
- }
- publicvoid setLevel(int level) {
- this.level = level;
- }
- public String getName() {
- return name;
- }
- publicvoid setName(String name) {
- this.name = name;
- }
- }
- // 巢穴
- class Nest {
- //我研究的龍之常數
- finalint DRAGON_M = 4162;
- // 寶藏
- privateint treasure;
- // 居住的龍的級別
- privateint level;
- Nest(int level) {
- this.level = level;
- this.treasure = level * level * DRAGON_M;
- }
- int getTreasure() {
- return treasure;
- }
- publicint getLevel() {
- return level;
- }
- publicvoid setLevel(int level) {
- this.level = level;
- this.treasure = level * level * DRAGON_M;
- }
- }
編譯並執行檢視結果:
Java程式碼
- Exception in thread "main" java.lang.NullPointerException
- 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程式碼
- import java.util.HashMap;
- import java.util.Map;
- publicclass Test {
- publicstaticvoid main(String[] args) {
- // 龍和它的巢穴對映表
- Map<dragon , Nest> map = new HashMap<dragon , Nest>();
- // 在Map中放入四隻克萊恩大陸上的龍
- map.put(new Dragon("銳刃", 98), new Nest(98));
- map.put(new Dragon("明鏡", 95), new Nest(95));
- map.put(new Dragon("碧雷", 176), new Nest(176));
- map.put(new Dragon("瑪烈", 255), new Nest(255));
- // 檢視寶藏
- System.out.println("碧雷巢穴中有多少寶藏:" + map.get(new Dragon("碧雷", 176)).getTreasure());
- }
- }
- // 龍
- class Dragon {
- Dragon(String name, int level) {
- this.level = level;
- this.name = name;
- }
- // 龍的名字
- private String name;
- // 龍的級別
- privateint level;
- publicint getLevel() {
- return level;
- }
- publicvoid setLevel(int level) {
- this.level = level;
- }
- public String getName() {
- return name;
- }
- publicvoid setName(String name) {
- this.name = name;
- }
- @Override
- publicint hashCode() {
- finalint PRIME = 31;
- int result = 1;
- result = PRIME * result + level;
- result = PRIME * result + ((name == null) ? 0 : name.hashCode());
- return result;
- }
- @Override
- publicboolean equals(Object obj) {
- if (this == obj)
- returntrue;
- if (obj == null)
- returnfalse;
- if (getClass() != obj.getClass())
- returnfalse;
- final Dragon other = (Dragon) obj;
- if (level != other.level)
- returnfalse;
- if (name == null) {
- if (other.name != null)
- returnfalse;
- } elseif (!name.equals(other.name))
- returnfalse;
- returntrue;
- }
- }
- // 巢穴
- class Nest {
- //我研究的龍之常數
- finalint DRAGON_M = 4162;
- // 寶藏
- privateint treasure;
- // 居住的龍的級別
- privateint level;
- Nest(int level) {
- this.level = level;
- this.treasure = level * level * DRAGON_M;
- }
- int getTreasure() {
- return treasure;
- }
- publicint getLevel() {
- return level;
- }
- publicvoid setLevel(int level) {
- this.level = level;
- this.treasure = level * level * DRAGON_M;
- }
- }
編譯並執行檢視結果:
Java程式碼
- 碧雷巢穴中有多少寶藏:128922112