Java 集合 Map Properties讀取屬性文件

map用於保存具備映射關係的數據,所以Map集合裏保存着兩組值,一組值用於保存Map裏的key,另一組值用於保存Map裏的value,key和value均可以是任何引用類型的數據。Map的key不容許重複,即同一個Map對象的任何兩個key經過equals方法比較老是返回false。key和value之間存在單向一對一關係,即經過指定的key,總能找到惟一的、肯定的value。從Map中取出數據時,只要給出指定的key,就能夠取出對應的value。java

Map有時也稱爲字典,或關聯數組。Map接口中定義以下方法:算法

  • void clear():刪除Map對象中全部key-value對數組

  • boolean containsKey(Object key):查詢Map中是否包含指定key,若是包含則返回true緩存

  • boolean containsValue(Object value):查詢Map中是否包含一個或多個value,若是包含則返回true安全

  • Set entrySet():返回Map中全部包含的key-value對組成的Set集合,每一個集合元素都是Map.Entry(Entry是Map的內部類)對象多線程

  • Object get(Obejct key):返回指定key所對應的value;若是此Map中不包含key,則返回nullapp

  • boolean isEmpty():查詢該Map是否爲空(即不包含任何key-value對),若是爲空則返回true函數

  • Set keySet():返回該Map中全部key所組成的set集合性能

  • Object put(Object key, Object value):添加一個key-value對,若是當前Map中已有一個與該key相等的key-value對,則新的key-value對會覆蓋原來的key-value對this

  • Object remove(Object key):刪除指定key對應的key-value對,返回被刪除key所關聯的value,若是該key不存在,返回null

  • int size():返回該Map裏的key-value對的個數

  • Collection values():返回該Map裏全部value組成的Collection

Map中包括一個內部類:Entry。該類封裝了一個key-value對,Entry包含三個方法:

  • Object getkey():返回該Entry裏包含的key值

  • Object getValue():返回該Entry裏包含的value值

  • Object setValue():設置該Entry裏包含的value值,並返回新設置的value值

import java.util.*;
    
    public class MapTest
    {
        public static void main(String[] args)
        {
            Map map = new HashMap();
            // 成對放入多個key-value對
            map.put("勒布朗詹姆斯", 6);
            map.put("凱文杜蘭特", 35);
            map.put("斯蒂芬庫裏", 30);
            // 屢次放入的key-value對中value能夠重複
            map.put("維斯布魯克", 0);
            // 放入重複的key時,新的value會覆蓋原有的value
            // 若是新的value覆蓋了原有的value,該方法返回被覆蓋的value
            System.out.println(map.put("勒布朗詹姆斯", 23)); // 輸出6
            System.out.println(map); // 輸出的Map集合包含4個key-value對
            // 輸出{凱文杜蘭特=35, 勒布朗詹姆斯=23, 斯蒂芬庫裏=30, 維斯布魯克=0}
            // 判斷是否包含指定key
            System.out.println("是否包含值爲 勒布朗詹姆斯 key:"
                + map.containsKey("勒布朗詹姆斯")); // 輸出true
            // 判斷是否包含指定value
            System.out.println("是否包含值爲 0 value:"
                + map.containsValue(0)); // 輸出true
            // 獲取Map集合的全部key組成的集合,經過遍歷key來實現遍歷全部key-value對
            for (Object key : map.keySet() )
            {
                // map.get(key)方法獲取指定key對應的value
                System.out.println(key + "-->" + map.get(key));
            }
            map.remove("凱文杜蘭特"); // 根據key來刪除key-value對。
            System.out.println(map); // 輸出結果再也不包含 瘋狂凱文杜蘭特=35 的key-value對
        }
}

Map的實現類都重寫了toString()方法,調用Map對象的toString()方法老是返回以下格式的字符串:{key1=value, key2=value...}

Java8爲Map新增的方法

Java8除了爲map增長了remove(Object key, Object value)默認方法以外,還增長了以下方法:

  • Object compute(Object key, BiFunction remappingFunction):該方法使用remappingFunction根據原key-value對計算一個新value。只要新value不爲null,就使用新value覆蓋原value;若是原value不爲null,但新value爲null,則刪除原key-value對;若是原value、新value同時爲null,那麼該方法不改變任何key-value對,直接返回null

  • Object computeIfAbsent(Object key, Function mappingFunction):若是傳給該方法的key參數在Map中對應的value爲null,則使用mappingFunction根據key計算一個新的結果,若是計算結果不爲null,則用計算結果覆蓋原有value。若是原Map原來不包含該key,那麼該方法可能會添加一組key-value對

  • Object computeIfPresent(Object key ,BiFunction remappingFunction):若是傳給該方法的key參數在Map中對應的value不爲null,該方法將使用remappingFunction根據原key、value計算一個新的結果,若是計算結果不爲null,則使用該結果覆蓋原來的value,若是計算的結果爲null,則刪除原key-value對

  • void forEach(BiConsumer action):該方法是Java 8爲Map新增的一個遍歷key-value對的方法

  • Object getOrDefault(Object key, V defaultValue):獲取指定key對應的value。若是該key不存在則返回defaultValue

  • Object merge(Object key, Object value, BiFunction remappingFunction):該方法會先根據Key參數獲取該Map中對應的value。若是得到value爲null,則直接用傳入的value覆蓋原有的value(在這中狀況下,可能要添加一組key-value對);若是獲取的value不爲null,則使用remappingFunction 函數根據原value、新value計算一新的結果,並用獲得的結果去覆蓋原有的value

  • Object putIfAbsent(Object key, Object value):該方法會自動檢測指定key對應的value是否爲null,若是該key對應的value爲null,該方法將會用新value代替原來的null值

  • Object replace(Object key, Object value):將Map中指定key對應的value替換成新的value。與傳統的put方法不一樣的是,該方法不可能添加新的key-value對。若是嘗試替換的key在原Map中不存在,該方法不會添加key-value對,而是返回null

  • boolean replace(K key, V oldValue, V newValue)
    將Map中指定key-value對的原value提換成新value。若是在Map中找到指定的key-value對,則執行替換並返回true,否額返回false

  • replaceAll(Bifunction function):該方法使用Bifunction 對原key-value對執行計算,並將計算結果做爲key-value對的value的值

import java.util.*;

public class MapTest2
{
    public static void main(String[] args)
    {
        Map map = new HashMap();
        // 成對放入多個key-value對
        map.put("勒布朗詹姆斯", 6);
        map.put("凱文杜蘭特", 35);
        map.put("斯蒂芬庫裏", 30);
        // 嘗試替換key爲"維斯布魯克"的value,因爲原Map中沒有對應的key,
        // 所以對Map沒有改變,不會添加新的key-value對
        map.replace("維斯布魯克", 0);
        System.out.println(map);
        // 使用原value與參數計算出來的結果覆蓋原有的value
        map.merge("勒布朗詹姆斯", 17 ,
            (oldVal , param) -> (Integer)oldVal + (Integer)param);
        System.out.println(map); // "勒布朗詹姆斯"的value增大了17,變爲23
        // 當key爲"詹姆斯哈登"對應的value爲null(或不存在時),使用計算的結果做爲新value
        map.computeIfAbsent("凱文加內特" , (key)->((String)key).length());
        System.out.println(map); // map中添加了 凱文加內特=5 這組key-value對
        // 當key爲"凱文加內特"對應的value存在時,使用計算的結果做爲新value
        map.computeIfPresent("凱文加內特",
            (key , value) -> (Integer)value + 16);
        System.out.println(map); // map中 凱文加內特=5 變成 凱文加內特=21
    }
}

Java8改進的HashMap和Hashtable實現類

HashMap和Hashtable都是Map接口的典型實現類,它們之間的關係徹底相似於ArrayList和Vector的關係

使用HashMap存在key衝突時依然具備較好的性能

  • Hashtable是一個線程安全的Map實現,但HashMap是線程不安全的實現,因此HashMap比Hashtable的性能高一點;但若是有多線程訪問同一個Map對象時,使用Hashtable實現類會更好

  • Hashtable不容許使用null做爲key和value,若是試圖把null值放進Hashtable中,將會引起NullPointerException異常;但HashMap可使用null做爲key或value

因爲HashMap裏的key不能重複,因此HashMap裏最多隻有一個key-value對的key爲null,但能夠有無數多個key-value對的value爲null

public class NullInHashMap
{
    public static void main(String[] args)
    {
        HashMap hm = new HashMap();
        // 試圖將兩個key爲null的key-value對放入HashMap中
        hm.put(null, null);
        hm.put(null, null);    // ①
        // 將一個value爲null的key-value對放入HashMap中
        hm.put("a", null);    // ②
        // 輸出Map對象
        System.out.println(hm);
    }
}

①代碼處沒法將key-value對放入,由於Map中已經有一個key-value對的key爲null,因此沒法再放入key爲null值的key-value對

{null=null, a=null}

爲了成功的在HashMap、Hashtable中存儲、獲取對象,用做key的對象必須實現hashCode()方法和equals()方法

與HashSet集合不能保證元素的順序同樣,HashMap、Hashtable也不能保證其中key-value對的順序。相似於HashSet的是,HashMap、Hashtable判斷兩個key相等的標準也是:兩個key經過equals方法比較返回true,兩個key的hashCode值也相等。除此以外,HashMap、Hashtable中還包含一個containsValue()方法用於判斷是否包含指定value。HashMap、Hashtable判斷兩個value相等的標準更簡單:只要兩個對象經過equals比較返回true便可

import java.util.Hashtable;

class A
{
    int count;
    public A(int count) 
    {
        this.count = count;
    }
    // 根據count的值來判斷兩個對象是否相等
    public boolean equals(Object obj) 
    {
        if (obj == this) 
        {
            return true;
        }
        if (obj != null && obj.getClass() == A.class) 
        {
            A a = (A)obj;
            return this.count == a.count; 
        }
        return false;
    }
    // 根據count來計算hashCode值
    public int hashCode() 
    {
        return this.count;
    }
}

class B
{
    // 重寫equals()方法,B對象與任何對象經過equals()方法比較都返回ture
    public boolean equals() 
    {
        return true;
    }
}

public class HashtableTest2 
{
    public static void main(String[] args) 
    {
        Hashtable ht = new Hashtable<>();
        ht.put(new A(23), "勒布朗詹姆斯");
        ht.put(new A(0), "凱文樂福");
        // ht.put(new A(2), "凱里歐文");
        ht.put(new A(6), new B());
        System.out.println(ht);
        // 只要兩個對象經過equals()方法比較返回true
        // Hashtable就認爲它們是相等的value
        // 因爲Hashtable中有一個B對象
        // 它與任何對象經過equals比較都相等,因此下面輸出true
        System.out.println(ht.containsValue("克利夫蘭騎士")); // ① 輸出true
        // 只要兩個A對象的count相等,它們經過equals比較返回true,且hashCode相等
        // Hashtable即認爲它們是相同的key,因此下面輸出true
        System.out.println(ht.containsKey(new A(23)));   // ② 輸出true
        // 下面語句能夠刪除最後一個key-value對
        ht.remove(new A(6));    //③
        System.out.println(ht);
    }
}

與HashSet相似的是,若是使用可變對象做爲HashMap、Hashtable的key,而且程序修改了做爲key的可變對象,可能引起與HashSet相似的情形:程序沒法準確訪問到Map中被修改過key。 因此說盡可能不要使用可變對象做爲HashMapHashtable的key

LinkedHashMap實現類

HashSet有一個子類是LinkedHashSet,HashMap也有一個LinkedHashMap子類;LinkedHashMap也使用雙向鏈表來維護key-value對的次序;該鏈表負責維護key的迭代順序,迭代順序與key-value的插入順序一致

LinkedHashMap能夠避免對HashMap、Hashtable裏的key-value對進行排序(只要插入key-value對時保持順序便可)。同時又可避免使用TreeMap所增長的成本

LinkedHashMap須要維護元素的插入順序,所以性能略低於HashMap的性能,但在迭代訪問Map裏的所有元素時將有很好的性能,由於它以鏈表來維護內部順序

public class LinkedHashMapTest
{
    public static void main(String[] args)
    {
        LinkedHashMap scores = new LinkedHashMap();
        scores.put("勒布朗詹姆斯", 23);
        scores.put("維斯布魯克", 0);
        scores.put("斯蒂芬庫裏", 30);
        // 調用forEach方法遍歷scores裏的全部key-value對
        scores.forEach((key, value) -> System.out.println(key + "-->" + value));
    }
}

使用Properties讀取屬性文件

Properties類是Hashtable類的子類,用於處理屬性文件(例如Windows操做平臺上的ini文件)。Properties類能夠把Map對象和屬性文件關聯起來,從而能夠把Map對象中的key-value對寫入屬性文件,也能夠把屬性文件中的「屬性名=屬性值」加載到Map對象中。因爲屬性文件裏的屬性名、屬性值只能是字符串類型,因此Properties裏的key、value都是字符串類型

修改Properties裏的key、value值的方法

  • String getProperty(String key):獲取Properties中指定屬性名對應的屬性值,相似於Map的get(Object key)方法

  • String getProperty(String key, String defaultValue):該方法與前一個方法基本相似。該方法多一個功能,若是Properties中不存在指定key時,該方法返回默認值

  • Object geProperty(String key、String value):設置屬性值,相似Hashtable的put方法

讀、寫屬性文件的方法:

  • void load(InputStream inStream):從屬性文件(以輸入流表示)中加載屬性名=屬性值,把加載到的屬性名=屬性值對追加到Properties裏(因爲Properties是Hashtable)的子類,它不保證key-value對之間的次序)

  • void Store(OutputStream out, String comment):將Properties中的key-valu對寫入指定屬性文件(以輸出流表示)

public class PropertiesTest
{
    public static void main(String[] args)
        throws Exception
    {
        Properties props = new Properties();
        // 向Properties中增長屬性
        props.setProperty("username" , "LeBron");
        props.setProperty("teams" , "Cavaliers");
        // 將Properties中的key-value對保存到a.ini文件中
        props.store(new FileOutputStream("NBA.ini")
            , "comment line");   //①
        // 新建一個Properties對象
        Properties props2 = new Properties();
        // 向Properties中增長屬性
        props2.setProperty("gender" , "male");
        // 將a.ini文件中的key-value對追加到props2中
        props2.load(new FileInputStream("NBA.ini") );   //②
        System.out.println(props2);
    }
}

SortedMap接口和TreeMap實現類

Map接口派生了一個SortedMap子接口,TreeMap爲其實現類。相似TreeSet排序,TreeMap也是基於紅黑樹對TreeMap中全部key進行排序,從而保證TreeMap中全部key-value對處於有序狀態。TreeMap兩種排序方法:

  • 天然排序:TreeMap的全部key必須實現Comparable接口,並且全部key應該是同一個類的對象,不然將會拋出ClassCastExcepiton異常

  • 定製排序:建立TreeMap時,傳入一個Comparator對象,該對象負責對TreeMap中全部key進行排序。採用定製排序時不要求Map的key實現Comparable接口

TreeMap中判斷兩個key相等的標準是:兩個key經過compareTo方法返回0,TreeMap即認爲這兩個key是相等的。

若是使用自定義的類做爲TreeMap的key,應從新該類的equals方法和compareTo方法時應有一致的返回結果:即兩個key經過equals方法比較返回true時,它們經過compareTo方法比較應該返回0。若是equals方法與compareTo方法的返回結果不一致,要麼該TreeMap與Map接口的規則有出入(當equals比較返回true,但CompareTo比較不返回0時),要麼TreeMap處理起來性能有所降低(當compareTo比較返回0,當equals比較不返回true時)

根據key順序來訪問Map中key-value對方法:

  • Map.Entry firstEntry():返回該Map中最小key所對應的key-value對,若是該Map爲空,則返回null

  • Map.Entry lastEntry():返回該Map中最大key所對應的key-value對,若是該Map爲空,或不存在這樣的key-value都返回null

  • Object firstKey():返回該Map中的最小key值,若是該Map爲空,則返回null

  • Object lastKey():返回該Map中的最大key值,若是該Map爲空,或不存在這樣的key都返回null

  • Map.Entry higherEntry(Object key):返回該Map中位於key後一位的key-value對(即大於指定key的最小key所對應的key-value對)。若是該Map爲空,則返回null

  • Map.Entry lowerEntry(Object key):返回該Map中位於key前一位的key-value對(即小於指定key的最大key所對應的key-value對)。若是該Map爲空,或不存在這樣的key-value則返回null

  • Object higherKey():返回該Map中位於key後一位的key值(即大於指定key的最小key值)。若是該Map爲空,或不存在這樣的key都返回null

  • Object lowerKey():返回該Map中位於key前一位的key值(即小於指定key的最大key值)。若是該Map爲空,或不存在這樣的key都返回null

  • NavigableMap subMap(Object fromKey, boolean fromInclusive, Object
    tokey, boolean tolnclusive):返回該Map的子Map,其key的範圍從fromKey(是否包括取決於第二個參數)到tokey(是否包括取決於第四個參數)。

  • NavigableMap headMap(Object toKey, boolean lnclusive):返回該Map的子Map,其key的範圍是小於toKey(是否包括取決於第二個參數)的全部key

  • NavigableMap tailMap(Object fromKey, boolean lnclusive):返回該Map的子Map,其key的範圍是大於fromKey(是否包括取決於第二個參數)的全部key

  • SorterMap subMap(Object fromKey, Object toKey):返回該Map的子Map,其key的範圍從fromKey(包括)到toKey(不包括)

  • SortedMap headMap(Object toKey):返回該Map的子Map,其key的範圍是小於tokey(是否包括取決於第二個參數)的全部key

  • SortedMap tailMap(Object fromKey):返回該Map的子Map,其key的範圍是大於fromkey(是否包括取決於第二個參數)的全部key

class R implements Comparable
{
    int count;
    public R(int count)
    {
        this.count = count;
    }
    public String toString()
    {
        return "R[count:" + count + "]";
    }
    // 根據count來判斷兩個對象是否相等。
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj != null    && obj.getClass() == R.class)
        {
            R r = (R)obj;
            return r.count == this.count;
        }
        return false;
    }
    // 根據count屬性值來判斷兩個對象的大小。
    public int compareTo(Object obj)
    {
        R r = (R)obj;
        return count > r.count ? 1 :
            count < r.count ? -1 : 0;
    }
}
public class TreeMapTest
{
    public static void main(String[] args)
    {
        TreeMap tm = new TreeMap();
        tm.put(new R(3) , "輕量級Java EE企業應用實戰");
        tm.put(new R(-5) , "瘋狂Java講義");
        tm.put(new R(9) , "瘋狂Android講義");
        System.out.println(tm);
        // 返回該TreeMap的第一個Entry對象
        System.out.println(tm.firstEntry());
        // 返回該TreeMap的最後一個key值
        System.out.println(tm.lastKey());
        // 返回該TreeMap的比new R(2)大的最小key值。
        System.out.println(tm.higherKey(new R(2)));
        // 返回該TreeMap的比new R(2)小的最大的key-value對。
        System.out.println(tm.lowerEntry(new R(2)));
        // 返回該TreeMap的子TreeMap
        System.out.println(tm.subMap(new R(-1) , new R(4)));
    }
}

WeakHashMap

HashMap中的key保存的是實際對象的強引用,這意味着只要該HashMap對象不被銷燬,該HashMap的全部key所引用的對象就不會被垃圾回收,HashMap也不會自動刪除這些key所對應的key-value對

WeakHashMap中的key保存的是實際對象的弱引用,這意味着只要該WeakHashMap對象沒被其餘強對象引用變量引用,則這些key所引用的對象可能被垃圾回收,就有可能會被垃圾回收機制回收對應的Key-value,WeakHashMap也可能自動刪除這些key所對應的key-value對

public class WeakHashMapTest
{
    public static void main(String[] args)
    {
        WeakHashMap whm = new WeakHashMap();
        // 將WeakHashMap中添加三個key-value對,
        // 三個key都是匿名字符串對象(沒有其餘引用)
        whm.put(new String("南特") , new String("Nantes"));
        whm.put(new String("巴黎") , new String("Paris"));
        whm.put(new String("波爾多") , new String("Bordeaux"));
        //將 WeakHashMap中添加一個key-value對,
        // 該key是一個系統緩存的字符串對象。
        whm.put("馬賽" , new String("Marseille"));    // ①
        // 輸出whm對象,將看到4個key-value對。
        System.out.println(whm);
        // 通知系統當即進行垃圾回收
        System.gc();
        System.runFinalization();
        // 一般狀況下,將只看到一個key-value對。
        System.out.println(whm);
    }
}

當系統進行垃圾回收時,刪除了WeakHashMap對象的前三個key-value對。由於添加前三個key-value對時,這三個key都是匿名的字符串對象,WeakHashMap只保留了對它們的弱引用,這樣垃圾回收時會自動刪除這三個key-value對。第4個key-value對的key是一個字符串直接量(系統會自動保留對該字符串對象的強引用),因此垃圾回收時不會回收它

IdentityHashMap實現類

IdentityHashMap的實現機制與HashMap基本類似,在IdentityHashMap中,判斷兩個key是否相等,是經過嚴格相等即(key1==key2)來判斷的,而HashMap是經過equals()方法和hashCode()這兩個方法來判斷key是否相等的。IdentityHashMap容許使用null做爲key和value,不保證key-value對之間的順序,不保證它們的順序隨時間的推移保持不變

public class IdentityHashMapTest
{
    public static void main(String[] args)
    {
        IdentityHashMap ihm = new IdentityHashMap();
        // 下面兩行代碼將會向IdentityHashMap對象中添加兩個key-value對
        ihm.put(new String("勒布朗詹姆斯") , 23);
        ihm.put(new String("勒布朗詹姆斯") , 6);
        // 下面兩行代碼只會向IdentityHashMap對象中添加一個key-value對
        ihm.put("科比布萊恩特" , 8);
        ihm.put("科比布萊恩特" , 24);
        System.out.println(ihm);
    }
}

前2個key-value對中的key是新建立的字符串對象,它們經過==比較不相等,因此IdentityHashMap會把它們當成2個key來處理;後2個key-value對中的key都是字符串直接量,並且它們的字符序列徹底相同,Java使用常量池來管理字符串直接量,因此它們經過==比較返回true,IdentityHashMap會認爲它們是同一個Key,所以只有一次能夠添加成功

EnumMap實現類

EnumMap是一個與枚舉類一塊兒使用的Map實現,EnumMap中的全部key都必須是單個枚舉類的枚舉值。建立EnumMap時必須顯式或隱式指定它對應的枚舉類。EnumMap具備以下特徵:

  • EnumMap在內部以數組形式保存,因此這種實現形式很是緊湊、高效

  • EunmMap根據key的天然順序(即枚舉值在枚舉類中的定義順序)來維護key-value對的順序。當程序經過keySet()、entrySet()、values()等方法遍歷EnumMap時能夠看到這種順序

  • EnumMap不容許使用null做爲key,但容許使用null做爲value。若是試圖使用null做爲key時將拋出NullpointerException。若是隻是查詢是否包含值爲null的key,或只是刪除值爲null的key,都不會拋出異常

enum NBA_Player
{
    James,Westbrook,Curry,Harden
}
public class EnumMapTest
{
    public static void main(String[] args)
    {
        // 建立EnumMap對象,該EnumMap的全部key都是Season枚舉類的枚舉值
        EnumMap enumMap = new EnumMap(NBA_Player.class);
        enumMap.put(NBA_Player.Westbrook, "俄克拉荷馬雷霆");
        enumMap.put(NBA_Player.James, "克利夫蘭騎士");
        System.out.println(enumMap);
    }
}

建立EnumMap對象時指定它的key只能是NBA_Player枚舉類的枚舉值。若是向該EnumMap中添加兩個key-value對後,這兩個key-value對將會以NBA_Player枚舉值的天然順序排序

各Map實現類的性能分析

HashMap和Hashtable的實現機制幾乎同樣,但因爲Hashtable是一個古老的、線程安全的集合,所以HashMap一般比Hashtable要快

TreeMap比HashMap和Hashtable要慢(尤爲在插入、刪除key-value對時更慢),由於TreeMap底層採用紅黑樹來管理key-value對(紅黑樹的每一個節點就是一個key-value對)
TreeMap中的key-value老是處於有序狀態,無需專門進行排序操做。當TreeMap被填充後,就能夠調用keySet(),取得由key組成的Set,而後使用toArray()方法生成key的數組,接下來使用Arrays的binarySearch()方法在已排序的數組中快速查詢對象

LinkedHashMap比HashMap慢一點,由於它須要維護鏈表來保持Map中key-value時的添加順序

IdentityHashMap性能沒有特別出色支持,採用與HashMap基本類似的實現,只是它使用==而不是equals()方法來判斷元素相等

EnumMap性能最好,但它只能使用同一個枚舉類的枚舉值做爲key

對於通常的應用場景,程序應該多考慮使用HashMap,由於HashMap正是爲快速查詢設計的(HashMap底層其實也是採用數組來存儲key-value對)。但若是程序須要一個老是排好序的Map時,則能夠考慮使用TreeMap

HashSet和HashMap的性能選項

對於HashSet及其子類而言,它們採用hash算法來決定集合中元素的存儲位置,並經過hash算法來控制集合的大小;
對於HashMap、Hashtable及其子類而言,它們採用hash算法來以爲Map中key的存儲,並經過hash算法來增長key集合的大小

hash表裏能夠存儲元素的位置被稱爲「桶(bucket)」,在一般狀況下,單個「桶」裏存儲一個元素時,此時擁有最好的性能:hash算法能夠根據hashCode值計算出「桶」的存儲位置,接着從「桶」中取出元素。但hash表的狀態是open的:在發生「hash衝突」的狀況下,單個桶會存儲多個元素,這些元素以鏈表形式存儲,必須按順序搜索。以下圖所示是hash表保存各元素

clipboard.png

由於HashSet和HashMap、Hashtable都使用hash算法來決定其元素(HashMap則只考慮key)的存儲,所以HashSet、HashMap的hash表中包含以下屬性

  • 容量(capacity):hash表中桶的數量

  • 初始化容量(inital capacity):建立hash表時桶的數量。HashMap和HashSet都容許在構造器中指定初始化容量

  • 尺寸(size):當前hash表中記錄的數量

  • 負載因子(load factor):負載因子等於「size/capacity」。負載因子爲0,表示空的hash表,0.5表示半滿的hash表,依次類推。輕負載的hash表具備衝突少,適宜插入與查詢的特色(可是用Iterator迭代元素時比較慢)

除此以外,hash表裏還有一個「負載極限」是一個0~1的數值,「負載極限」決定了hash表的最大填滿程序。
當hash表中的負載因子達到指定的「負載極限」時,hash表會自動成倍地增長容量(桶的數量),並將原有的對象從新分配,嵌入新的桶內,這稱爲rehashing

HashSet和HashMap、Hashtable的構造器容許指定一個負載極限,HashSet和HashMap、Hashtable默認的「負載極限」爲0.75,這代表當該hash表的3/4已經被填滿時,hash表會發生rehashing

「負載極限」的默認值(0.75)是時間和空間成本上的一種折中:較高的「負載極限」能夠下降hash表所佔用的內存空間,但會增長查詢數據和時間的開銷,而查詢是最頻繁的操做(HashMap的get()和put()方法都要用到查詢);
較低的「負載極限」會提升查詢數據的性能,但會增長hash表所佔用的內存開銷。

若是開始就知道HashSet和HashMap、Hashtable會保存不少記錄,則能夠在建立時就使用較大的初始化容量,若是初始化容量始終大於HashSet、Hashtable和HashMap所包含的最大記錄數除以「負載極限」,就不會發生rehashing。使用足夠大的初始化容量建立HashSet和HashMap、Hashtable時,能夠更高效地增長記錄,但將初始化容量設置過高會浪費空間,因此不要講初始化容量設置太高

相關文章
相關標籤/搜索