Java集合與框架總結與學習

林炳文Evankaka原創作品。轉載請註明出處http://blog.csdn.net/evankaka
本文將主要講解Java中集合的使用與區別,主要講List、Set、Map的原理、使用方法、注意事項等。
一、Collection與Collections的區別
Java集合框架是Java語言的重要組成部分,它包含了系統而完整的集合層次體系,封裝了大量的數據結構的實現。深刻理解Java集合框架的組成結構及其中的實現類和算法,能極大提高程序員編碼的能力。本章講述Java集合框架,主要包括集合框架的概念、集合框架接口,以及列表、集合、映射三種結構還有迭代方法、比較方法和較早以前版本的類和接口。下面就讓我們一起來學習這些內容。
集合有時又稱爲容器,簡單地說,它是一個對象,能將具有相同性質的多個元素匯聚成一個整體。集合被用於存儲、獲取、操縱和傳輸聚合的數據。Java集合框架提供了有效的數據結構和算法,因此程序員不需要自己編寫代碼實現這些功能。而且結合框架對各個接口的實現是可以互換的,因此很容易轉換接口。這樣就提高了軟件的複用性。

注意事項:

(1)Collection是集合類的一個頂級接口,其直接繼承接口有List與Set

(2)Collections則是集合類的一個工具類/幫助類,其中提供了一系列靜態方法,用於對集合中元素進行排序、搜索以及線程安全等各種操作。
二、List、Set、Map三大集合區別與說明
Java平臺提供了一個全新的集合框架。集合框架的核心接口爲Collection、List(列表)、Set(集合)和Map(映射)。

這裏寫圖片描述

          圖1  集合關係圖
從圖1中可以看到,Collection是集合繼承樹中最頂層的接口,幾乎所有的Java集合框架成員都繼承實現了Collection接口,或者與其有密切關係。Collection提供了關於集合的通用操作。Set接口和List接口都繼承了Collection接口,而Map接口沒有繼承Collection接口。因此,Set對象和List對象都可以調用Collection接口的方法,而Map對象則不可以。
下面我們對這三種類型接口的結構加以說明:Set有點類似數學中集合的定義,是無序的、沒有重複項目的集合。List是位置性集合,加進清單的元素可以加在清單中特定位置或加到末尾,可以保存重複的元素。Map用於關鍵字/數值對,其中關鍵字是數值的惟一標識(不可重複),用戶可以按關鍵字存取數據,數據可重複。具體形式如圖2所示:

這裏寫圖片描述

圖2 各集合元素存放示例

(1)Collection:集合層次中的根接口,JDK 沒有提供這個接口直接的實現類。

(2)Set:不能包含重複的元素。

(3) List:是一個有序的集合,可以包含重複的元素。提供了按索引訪問的方式。

(4)Map:包含了 key-value 對。Map 不能包含重複的 key。

三、List用法總結
3.1 List接口

List的主要特徵使其元素已先行方式存儲,集合中允許存放重複對象。List接口主要的實現類包括:

(1)ArrayList:代表長度可變的數組。允許對元素進行快速的隨機訪問,但是向ArrayList中插入與刪除元素的速度較慢。

(2)LinkedList :在實現中採用鏈表數據結構,元素之間是雙鏈接。對順序訪問進行了優化,向List中插入和刪除元素的速度較快,隨機訪問速度則相對較慢,隨機訪問是指檢索位於特定索引位置元素,當需要快速插入和刪除時LinkedList成爲List中的不二選擇。

(3)Vector :是ArrayList的線程安全版本,性能比ArrayList要低,現在已經很少使用。

3.2 ArrayList用法

3.2.1、ArrayList是個集合

  集合是可以往裏面添東西的,用add(E e)方法往裏面加(把E都看做Object)

  例子:ArrayList類中的add和get方法(add方法向集合中添加數據,get方法將集合中指定下標位置的數據取出)

 (1)size()方法,返回集合中的元素個數,類似於數組中的length屬性

 (2)clear()方法,將集合中的所有元素都給清除

 (3)isEmpty()方法判斷集合是否爲空,爲空返回true

 (4)remove(int index)方法是刪除集合中指定位置的元素,清除的原理是你清除了第一個後,後面的元素往前移,第一個變第0個了,原來第三個元素就沒啦

 (5)remove(Object ob)是remove的重載,一個是根據索引來刪除,一個是根據具體對象來刪除,比如remove("Hello");

  (6)indexOf(Obejct ob)判斷某個元素在索引的第幾個位置上。

在數組中查看數組元素個數,用數組的length屬性,在集合中查看集合元素個數,用集合的size()方法。

3.2.2、ArrayList使用範例

[java] view plain copy

import java.awt.List;  
import java.util.ArrayList;  
import java.util.Iterator;  
/** 
 * @author 林炳文 
 * @time 2015/2/5 
 * ArrayList用法示例說明 
 *  
 */  

public class Main {  
    public static void main(String[] args) {  
        //ArrayList用法示例  
        ArrayList<String> m_ArrayList=new ArrayList<String>();  
        m_ArrayList.add("Evankaka");  
        m_ArrayList.add("林炳文");  
        m_ArrayList.add("德德");  
        m_ArrayList.add("Evankaka");  
        m_ArrayList.add("小紅");  
        m_ArrayList.set(2,"文炳林");// 將索引位置爲2的對象修改  
        m_ArrayList.add(3,"秀秀");// 將對象添加到索引位置爲3的位置  

        //ArrayList遍歷方法1  
        Iterator<String> it_ArrayList = m_ArrayList.iterator();  
        System.out.println("ArrayList遍歷方法1");  
        while (it_ArrayList.hasNext()) {  
        System.out.println(it_ArrayList.next());  
        }  

        //ArrayList遍歷方法2  
        System.out.println("ArrayList遍歷方法2");  
        for(Object o:m_ArrayList){  
            System.out.println(o);  
        }  

        //ArrayList遍歷方法2  
        System.out.println("ArrayList遍歷方法3");  
        for(int i = 0; i<m_ArrayList.size(); i++){  
            System.out.println(m_ArrayList.get(i));  
            }  
        //刪除元素  
        m_ArrayList.remove("Evankaka");  
        it_ArrayList = m_ArrayList.iterator();  
        System.out.println("ArrayList刪除元素後的遍歷");  
        while (it_ArrayList.hasNext()) {  
            String m_String=it_ArrayList.next();  
         if(m_String.equals("秀秀")){  
             it_ArrayList.remove();  
         }else{  
        System.out.println(m_String);  
          }  
        }  
    }     
}  

輸出結果:

ArrayList遍歷方法1
Evankaka
林炳文
文炳林
秀秀
Evankaka
小紅
ArrayList遍歷方法2
Evankaka
林炳文
文炳林
秀秀
Evankaka
小紅
ArrayList遍歷方法3
Evankaka
林炳文
文炳林
秀秀
Evankaka
小紅
ArrayList刪除元素後的遍歷
林炳文
文炳林
Evankaka
小紅

3.2.3、ArrayList注意

(1)使用Iterator迭代集合過程中,不可修改集合元素,否則會引發異常。並且Iterator只能向後迭代

(2)如果你想在循環過程中去掉某個元素,只能調用it.remove方法, 不能使用list.remove方法, 否則一定出併發訪問的錯誤.
3.3 LinkedList用法

    LinkedList類是鏈表節點各種操作的實現,LinkedList類實現了一個帶有頭尾引用的通用型雙向鏈表。注意,此實現不是同步的。如果多個線程同時訪問列表,而其中至少一個線程從結構上修改了該列表,則它必須保持外部同步。(結構修改指添加或刪除一個或多個元素的任何操作;僅設置元素的值不是結構修改。)這一般通過對自然封裝該列表的對象進行同步操作來完成。如果不存在這樣的對象,則應該使用 Collections.synchronizedList 方法來“包裝”該列表。最好在創建時完成這一操作,以防止對列表進行意外的不同步訪問。

四、set接口

   set集合中多個對象之間沒有明顯的順序,set與Collection的結構基本上完全一樣,不同在於set不能包含重複元素。Set判斷兩個對象相同不是使用==運算符,而是根據equals方法。也就是說主要兩個對象用equals方法比較返回true,Set就不會接受這兩個對象。 HashSet是Set接口的典型實現,大多數時候使用Set集合就是使用這個類。

4.1 HashSet說明與範例

  HashSet按hash算法來存儲集合中的元素,因此具有很好的存儲和查找性能。 
  HaseSet具有以下特點: 
 ● 不能保證元素的排列順序,順序有可能發生變化 
● HashSet不是同步的,如果多個線程訪問同一個HashSet,要注意線程安全問題 
● 集合元素值可以爲null 
  當向HashSet集合中存入一個元素時,HashSet會調用該對象的hashCode()方法來得到該對象的hashCode值,然後根據該值來決定該對象在HashSet中的存儲位置。如果有兩個元素通過equals方法比較返回true,但它們的hashCode方法返回值不相等,HashSet將會把它們存儲在不同的位置。也就是說HashSet集合判斷兩個元素相等的標準是兩個對象通過equals方法比較相等,並且兩個對象的hashCode方法返回值也相等。

使用範例:

[java] view plain copy

package com.collection;  
import java.util.Date;  
import java.util.HashSet;  
import java.util.Iterator;  
public class Main  
{  
    public static void main(String [] args)  
    {  
       HashSet h=new HashSet();  
       h.add("1st");  
       h.add("2nd");  
       h.add(new Integer(3));  
       h.add(new Double(4.0));  
       h.add("2nd");            //重複元素,未被添加  
       h.add(new Integer(3));      //重複元素,未被添加  
       h.add(new Date());  
       System.out.println("開始:size="+h.size());  
       Iterator it=h.iterator();  
       while(it.hasNext())  
       {  
           Object o=it.next();  
           System.out.println(o);  
       }  
       h.remove("2nd");  
       System.out.println("移除元素後:size="+h.size());  
       System.out.println(h);  
    }  
}  

結果:

開始:size=5
4.0
1st
3
2nd
Mon Feb 16 20:52:17 CST 2015
移除元素後:size=4
[4.0, 1st, 3, Mon Feb 16 20:52:17 CST 2015]

4.2 TreeSet

   TreeSet描述的是Set的一種變體——可以實現排序等功能的集合,它在將對象元素添加到集合中時會自動按照某種比較規則將其插入到有序的對象序列中,並保證該集合元素組成時刻按照“升序”排列。

使用範例:

[java] view plain copy

package com.collection;  
import java.util.TreeSet;  
import java.util.Iterator;  
public class Main  
{  
    public static void main(String [] args)  
    {  
       TreeSet ts=new TreeSet();  
       ts.add("orange");  
       ts.add("apple");  
       ts.add("banana");  
       ts.add("grape");  
       Iterator it=ts.iterator();  
       while(it.hasNext())  
       {  
           String fruit=(String)it.next();  
           System.out.println(fruit);  
      }  
    }  
}  

結果:

apple
banana
grape
orange
4.3 Set總結
(1) HashSet和TreeSet是Set的兩個典型實現。HashSet的性能總是比TreeSet好(特別是常用的添加、查詢等操作),因爲TreeSet需要額外的紅黑樹算法來維護集合元素的次序。只有當需要一個保存排序的Set時,才應該使用TreeSet,否則都應該使用HashSet。
(2) LinkedHashSet是HashSet的子類,該集合也是根據元素hashCode值來決定元素存儲位置,但它同時使用鏈表維護元素的次序,這樣使得元素看起來是以插入的順序保存的。LinkedHashSet需要維護元素的插入順序,因此性能略低於HashSet的性能,但在迭代訪問Set裏的全部元素時將有很好的性能,又因爲鏈表的存在,遍歷LinkedHashSet會更快。
(3)TreeSet是SortedSet接口的實現,TreeSet可以確保集合元素處於排序狀態,TreeSet並不是根據元素的插入順序進行排序,而是根據元素的實際值來進行排序的。
(4)EnumSet是所有Set實現類中性能最好的,但它只能保存同一個枚舉類的枚舉值作爲集合元素。
(5)必須指出的是Set的三個實現類HashSet、TreeSet和EnumSet都是線程不安全的,如果有多條線程同時訪問一個Set集合,並有超過一條線程修改了該Set集合,則必須手動保證該Set集合的同步性。通常可以通過Collections工具類的synchronizedSortedSet方法來包裝該Set集合。此操作最好在創建時進行。

五 Map接口

 將鍵映射到值的對象。一個映射不能包含重複的鍵;每個鍵最多隻能映射到一個值。此接口取代 Dictionary 類,後者完全是一個抽象類,而不是一個接口。

  Map 接口提供三種collection 視圖,允許以鍵集、值集或鍵-值映射關係集的形式查看某個映射的內容。映射順序 定義爲迭代器在映射的 collection 視圖上返回其元素的順序。某些映射實現可明確保證其順序,如 TreeMap 類;另一些映射實現則不保證順序,如HashMap 類。

  注:將可變對象用作映射鍵時必須格外小心。當對象是映射中某個鍵時,如果以影響 equals 比較的方式更改了對象的值,則映射的行爲將是不確定的。此項禁止的一種特殊情況是不允許某個映射將自身作爲一個鍵包含。雖然允許某個映射將自身作爲值包 含,但請格外小心:在這樣的映射上 equals 和 hashCode 方法的定義將不再是明確的。

  所有通用的映射實現類應該提供兩個“標準的”構造方法:一個 void (無 參數)構造方法,用於創建空映射;一個是帶有單個 Map 類型參數的構造方法,用於創建一個與其參數具有相同鍵-值映射關係的新映射。實際上,後一個構造方法允許用戶複製任意映射,生成所需類的一個等價映射。盡 管無法強制執行此建議(因爲接口不能包含構造方法),但是 JDK 中所有通用的映射實現都遵從它。

  此接口中包含的“破壞”方法可修改其操作的映射,如果此映射不支持該操作,這些方法將拋出 UnsupportedOperationException。如果是這樣,那麼在調用對映射無效時,這些方法可以(但不要求)拋出 UnsupportedOperationException。例如,如果某個不可修改的映射(其映射關係是“重疊”的)爲空,則對該映射調用 putAll(Map) 方法時,可以(但不要求)拋出異常。

  某些映射實現對可能包含的鍵和值有所限制。例如,某些實現禁止 null 鍵和值,另一些則對其鍵的類型有限制。嘗試插入不合格的鍵或值將拋出一個未經檢查的異常,通常是 NullPointerException 或 ClassCastException。試圖查詢是否存在不合格的鍵或值可能拋出異常,或者返回 false;某些實現將表現出前一種行爲,而另一些則表現後一種。一般來說,試圖對不合格的鍵或值執行操作且該操作的完成不會導致不合格的元素被插入映射 中時,將可能拋出一個異常,也可能操作成功,這取決於實現本身。這樣的異常在此接口的規範中標記爲“可選”。

  此接口是 Java Collections Framework 的成員。

  Collections Framework 接口中的很多方法是根據 equals 方法定義的。例如,containsKey(Object key) 方法的規範中寫道:“當且僅當此映射包含針對滿足 (key==null ? k==null : key.equals(k)) 的鍵 k 的映射關係時,返回 true”。不 應將此規範解釋爲:調用具有非空參數 key 的 Map.containsKey 將導致對任意的鍵 k 調用 key.equals(k)。實現可隨意進行優化,以避免調用 equals,例如,可首先比較兩個鍵的哈希碼(Object.hashCode() 規範保證哈希碼不相等的兩個對象不會相等)。一般來說,只要實現者認爲合適,各種 Collections Framework 接口的實現可隨意利用底層 Object 方法的指定行爲。

Map 提供了一個更通用的元素存儲方法。Map 集合類用於存儲元素對(稱作“鍵”和“值”),其中每個鍵映射到一個值。從概念上而言,您可以將 List 看作是具有數值鍵的 Map。而實際上,除了 List 和 Map 都在定義 java.util 中外,兩者並沒有直接的聯繫。本文將着重介紹核心 Java 發行套件中附帶的 Map,同時還將介紹如何採用或實現更適用於您應用程序特定數據的專用 Map。

5.1、瞭解 Map 接口和方法

Java 核心類中有很多預定義的 Map 類。在介紹具體實現之前,我們先介紹一下 Map 接口本身,以便了解所有實現的共同點。Map 接口定義了四種類型的方法,每個 Map 都包含這些方法。下面,我們從兩個普通的方法開始對這些方法加以介紹。

表 1:覆蓋的方法。我們將這 Object 的這兩個方法覆蓋,以正確比較 Map 對象的等價性。
equals(Object o) 比較指定對象與此 Map 的等價性
hashCode() 返回此 Map 的哈希碼

這裏寫圖片描述

5.2、Map 構建

Map 定義了幾個用於插入和刪除元素的變換方法。

表 2:Map 更新方法: 可以更改 Map 內容。

clear() 從 Map 中刪除所有映射
remove(Object key) 從 Map 中刪除鍵和關聯的值
put(Object key, Object value) 將指定值與指定鍵相關聯
clear() 從 Map 中刪除所有映射
putAll(Map t) 將指定 Map 中的所有映射覆制到此 map

儘管您可能注意到,縱然假設忽略構建一個需要傳遞給 putAll() 的 Map 的開銷,使用 putAll() 通常也並不比使用大量的 put() 調用更有效率,但 putAll() 的存在一點也不稀奇。這是因爲,putAll() 除了迭代 put() 所執行的將每個鍵值對添加到 Map 的算法以外,還需要迭代所傳遞的 Map 的元素。但應注意,putAll() 在添加所有元素之前可以正確調整 Map 的大小,因此如果您未親自調整 Map 的大小(我們將對此進行簡單介紹),則 putAll() 可能比預期的更有效。

5.3、查看 Map

迭代 Map 中的元素不存在直接了當的方法。如果要查詢某個 Map 以瞭解其哪些元素滿足特定查詢,或如果要迭代其所有元素(無論原因如何),則您首先需要獲取該 Map 的“視圖”。有三種可能的視圖。

所有鍵值對 — 參見 entrySet()
所有鍵 — 參見 keySet()
有值 — 參見 values()

前兩個視圖均返回 Set 對象,第三個視圖返回 Collection 對象。就這兩種情況而言,問題到這裏並沒有結束,這是因爲您無法直接迭代 Collection 對象或 Set 對象。要進行迭代,您必須獲得一個 Iterator 對象。因此,要迭代 Map 的元素,必須進行比較煩瑣的編碼

Iterator keyValuePairs = aMap.entrySet().iterator();
Iterator keys = aMap.keySet().iterator();
Iterator values = aMap.values().iterator();

值得注意的是,這些對象(Set、Collection 和 Iterator)實際上是基礎 Map 的視圖,而不是包含所有元素的副本。這使它們的使用效率很高。另一方面,Collection 或 Set 對象的 toArray() 方法卻創建包含 Map 所有元素的數組對象,因此除了確實需要使用數組中元素的情形外,其效率並不高。
5.4、遍歷map

[java] view plain copy

<pre code_snippet_id="605637" snippet_file_name="blog_20150712_4_7253741" name="code" class="java">package com.test;   

import java.util.HashMap;  
import java.util.Iterator;  
import java.util.Map;  

public class Test {     

    public static void main(String[] args) {     
        Map<String, String> map = new HashMap<String, String>();     
        map.put("first", "linlin");     
        map.put("second", "bingbing");     
        map.put("third", "wenwen");    
        map.put("first", "linlin2");   


        // 第一種:通過Map.keySet遍歷key和value     
        System.out.println("===================通過Map.keySet遍歷key和value:===================");     
        for (String key : map.keySet()) {     
            System.out.println("key= " + key + "  and  value= " + map.get(key));     
        }     

        // 第二種:通過Map.entrySet使用iterator遍歷key和value     
        System.out.println("===================通過Map.entrySet使用iterator遍歷key和value:===================");     
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();     
        while (it.hasNext()) {     
            Map.Entry<String, String> entry = it.next();     
            System.out.println("key= " + entry.getKey() + "  and  value= "    
                    + entry.getValue());     
        }     

        // 第三種:通過Map.entrySet遍歷key和value     
        System.out.println("===================通過Map.entrySet遍歷key和value:===================");     
        for (Map.Entry<String, String> entry : map.entrySet()) {     
            System.out.println("key= " + entry.getKey() + "  and  value= "    
                    + entry.getValue());     
        }     

        // 第四種:通過Map.values()遍歷所有的value,但是不能遍歷鍵key     
        System.out.println("===================通過Map.values()遍歷所有的value:===================");     
        for (String v : map.values()) {     
            System.out.println("value= " + v);     
        }     
    }     

}    
</pre><br>  

輸出結果如下
===================通過Map.keySet遍歷key和value:===================
key= third and value= wenwen
key= first and value= linlin2
key= second and value= bingbing
===================通過Map.entrySet使用iterator遍歷key和value:===================
key= third and value= wenwen
key= first and value= linlin2
key= second and value= bingbing
===================通過Map.entrySet遍歷key和value:===================
key= third and value= wenwen
key= first and value= linlin2
key= second and value= bingbing
===================通過Map.values()遍歷所有的value:===================
value= wenwen
value= linlin2
value= bingbing

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章