欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中詳細(xì)解析Map接口

 更新時(shí)間:2021年08月10日 17:19:58   作者:IT劉華強(qiáng)  
這篇文章主要介紹了Java8 中 Map 接口的新方法,本文通過代碼實(shí)例給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

Map詳解:

先看圖,便于宏觀了解Map的地位。

Map接口中鍵和值一一映射. 可以通過鍵來獲取值。

  • 給定一個(gè)鍵和一個(gè)值,你可以將該值存儲在一個(gè)Map對象. 之后,你可以通過鍵來訪問對應(yīng)的值。
  • 當(dāng)訪問的值不存在的時(shí)候,方法就會拋出一個(gè)NoSuchElementException異常.
  • 當(dāng)對象的類型和Map里元素類型不兼容的時(shí)候,就會拋出一個(gè) ClassCastException異常。
  • 當(dāng)在不允許使用Null對象的Map中使用Null對象,會拋出一個(gè)NullPointerException 異常。
  • 當(dāng)嘗試修改一個(gè)只讀的Map時(shí),會拋出一個(gè)UnsupportedOperationException異常。

Map基本操作:

Map 初始化

Map<String, String> map = new HashMap<String, String>();

插入元素

map.put("key1", "value1");

獲取元素

map.get("key1")

移除元素

map.remove("key1");

清空map

map.clear();

hashMap原理:

hashMap是由數(shù)組和鏈表這兩個(gè)結(jié)構(gòu)來存儲數(shù)據(jù)。

數(shù)組:存儲區(qū)間是連續(xù)的,占用內(nèi)存嚴(yán)重,故空間復(fù)雜的很大。但數(shù)組的二分查找時(shí)間復(fù)雜度小,為O(1);尋址容易,插入和刪除困難;

鏈表:存儲區(qū)間離散,占用內(nèi)存比較寬松,故空間復(fù)雜度很小,但時(shí)間復(fù)雜度很大,達(dá)O(N);尋址困難,插入和刪除容易。

hashMap則結(jié)合了兩者的優(yōu)點(diǎn),既滿足了尋址,又滿足了操作,為什么呢?關(guān)鍵在于它的存儲結(jié)構(gòu)。

它底層是一個(gè)數(shù)組,數(shù)組元素就是一個(gè)鏈表形式,見下圖:

Entry: 存儲鍵值對。

Map類在設(shè)計(jì)時(shí)提供了一個(gè)靜態(tài)修飾接口Entry。Entry將鍵值對的對應(yīng)關(guān)系封裝成了鍵值對對象,這樣我們在遍歷Map集合時(shí),就可以從每一個(gè)鍵值對對象中獲取相應(yīng)的鍵與值。之所以被修飾成靜態(tài)是為了可以用類名直接調(diào)用。

每次初始化HashMap都會構(gòu)造一個(gè)table數(shù)組,而table數(shù)組的元素為Entry節(jié)點(diǎn),它里面包含了鍵key,值value,下一個(gè)節(jié)點(diǎn)next,以及hash值。

static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;
}

查看hashMap的API發(fā)現(xiàn),它有4個(gè)構(gòu)造函數(shù):

1、構(gòu)造一個(gè)具有默認(rèn)初始容量 (16) 和默認(rèn)加載因子 (0.75) 的空 HashMap。

2、指定初始容量和默認(rèn)加載因子 (0.75) 的空 HashMap。

3、指定初始容量和默認(rèn)加載因子的空HashMap。

4、構(gòu)造一個(gè)映射關(guān)系與指定Map相同的新HashMap。

注意:HashMap使用的是懶加載,構(gòu)造完HashMap對象后,只要不進(jìn)行put方法插入元素之前,HashMap并不會去初始化或者擴(kuò)容table。

Put方法:

首先判斷是否是空數(shù)組(table == EMPTY_TABLE),如果是,開始初始化HashMap的table數(shù)據(jù)結(jié)構(gòu),然后執(zhí)行擴(kuò)容函數(shù),如果未指定容量,默認(rèn)是大小為16的表,然后根據(jù)加載因子計(jì)算臨界值。什么是加載因子呢?hashMap的大小是一定的,如果不夠存儲了肯定要擴(kuò)容,那么擴(kuò)容的依據(jù)是什么呢,什么時(shí)候確定要擴(kuò)容了呢?這個(gè)時(shí)候就需要引入加載因子這個(gè)概念,我們假使依舊使用默認(rèn)大小16,加載因子0.75,那么當(dāng)hashMap的size大于12(16*0.75=12)的時(shí)候,那么就會進(jìn)行擴(kuò)容。

回來說put方法,如果key是null,調(diào)用putForNullKey方法,保存null與key,這是HashMap允許為null的原因。然后計(jì)算hash值和用indexFor計(jì)算數(shù)據(jù)存在的位置,然后從i出開始迭代e,找到 key 保存的位置。

上面說到如果數(shù)組擴(kuò)容,那么每次要怎么擴(kuò)容呢?

當(dāng)size大于等于某一個(gè)閾值thresholdde時(shí)候且該table并不是一個(gè)空table,因?yàn)閟ize 已經(jīng)大于等于閾值了,說明Entry數(shù)量較多,哈希沖突嚴(yán)重,那么若該Entry對應(yīng)的桶不是一個(gè)空桶,這個(gè)Entry的加入必然會把原來的鏈表拉得更長,因此需要擴(kuò)容;若對應(yīng)的桶是一個(gè)空桶,那么此時(shí)沒有必要擴(kuò)容。如果擴(kuò)容,table會擴(kuò)容為原來的兩倍,直到達(dá)到數(shù)組的最大長度1<<30(2的30次方),如果size大于這個(gè)值,那么就直接修改為Integer.MAX_VALUE。擴(kuò)容后的元素hash值對應(yīng)的新的桶位置,然后在指定的桶位置上,創(chuàng)建一個(gè)新的Entry。

這里需要說明的是,hashmap是可以存放key和value均為null的,存放在table[0]的位置,此時(shí)使用put方法在添加元素的時(shí)候,如果在table[0]中已經(jīng)存入key為null的元素則給null賦上新的value值并返回后面的值,否則則初始化null的元素,存入put里面存放的值。

public static void main(String[] args) {
        HashMap hashMap = new HashMap();
        hashMap.put(null, null);
        System.out.println(hashMap.get(null));
        Integer a = (Integer) hashMap.put(null, 1);
        System.out.println(a);
        System.out.println(hashMap.get(null));
}

輸出為:
null
null
1

Get方法:

Get比較好理解,判斷key是不是null,如果是,返回getForNullKey的函數(shù)返回值,如果不是,則在table中去找。

Remove方法:

判斷,如果hashMap的size是0,返回null;找到需要移除的元素的前一個(gè)節(jié)點(diǎn),然后把前驅(qū)節(jié)點(diǎn)的next指向刪除節(jié)點(diǎn)的next節(jié)點(diǎn),此時(shí)當(dāng)前節(jié)點(diǎn)沒有任何引用指向,它在程序結(jié)束之后就會被gc回收。

final Entry<K,V> removeEntryForKey(Object key) {
    		if (size == 0) {
    		    return null;
    		}
   		 int hash = (key == null) ? 0 : hash(key);
   		 int i = indexFor(hash, table.length);
    		Entry<K,V> prev = table[i];
    		Entry<K,V> e = prev;
		 while (e != null) {
        		Entry<K,V> next = e.next;
       		 Object k;
       		 if (e.hash == hash &&
        		    ((k = e.key) == key || (key != null && key.equals(k)))) {
        		    modCount++;
         		   size--;
         		   if (prev == e)
          		      table[i] = next;
         		   else
          		      prev.next = next;
          		  e.recordRemoval(this);
         		   return e;
        		}
        		prev = e;
        		e = next;
   		 }
		 return e;
	}

Map的遍歷:

map這里可以用增強(qiáng)for和迭代器兩種方式遍歷:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class MapDemo {
    public static void main(String[] args) {
        HashMap<String, String> sets = new HashMap<>();
        sets.put("username", "value1");
        sets.put("password", "value2");
        sets.put("key3", "value3");
        sets.put("key4", "value4");
        sets.put(null,null);
        // 增強(qiáng)for循環(huán) =========== keySet ===================
        for (String s : sets.keySet()) {
            System.out.println(s + ".." + sets.get(s));
        }
        //================== entrySet ======================
        for (Map.Entry<String, String> m : sets.entrySet()) {
            System.out.println(m.getKey() + ".." + m.getValue());
        }
        // 迭代器 ================ keySet ===================
        Iterator it = sets.keySet().iterator();
        while (it.hasNext()) {
            String key = (String) it.next();
            System.out.println(key + ".." + sets.get(key));
        }
        //================== entrySet ======================
        Iterator<Map.Entry<String, String>> iterator = sets.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> m = iterator.next();
            System.out.println(m.getKey() + ".." + m.getValue());
        }
    }
}

TreeMap

這里簡要介紹下:TreeMap 是一個(gè)有序的key-value集合,繼承于AbstractMap,它是通過紅黑樹實(shí)現(xiàn)的。TreeMap 實(shí)現(xiàn)了NavigableMap接口,實(shí)現(xiàn)了Cloneable接口,實(shí)現(xiàn)了java.io.Serializable接口。

TreeMap基于紅黑樹(Red-Black tree)實(shí)現(xiàn)。該映射根據(jù)其鍵的自然順序進(jìn)行排序,或者根據(jù)創(chuàng)建映射時(shí)提供的 Comparator 進(jìn)行排序,具體取決于使用的構(gòu)造方法。TreeMap的基本操作 containsKey、get、put 和 remove 的時(shí)間復(fù)雜度是 log(n) 。另外,TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fastl的。

紅黑樹(Red Black Tree) 是一種自平衡二叉查找樹,是在計(jì)算機(jī)科學(xué)中用到的一種數(shù)據(jù)結(jié)構(gòu),典型的用途是實(shí)現(xiàn)關(guān)聯(lián)數(shù)組。它有五個(gè)特點(diǎn)如下:

性質(zhì)1:節(jié)點(diǎn)是紅色或黑色。

性質(zhì)2:根節(jié)點(diǎn)是黑色。

性質(zhì)3:每個(gè)葉節(jié)點(diǎn)(NIL節(jié)點(diǎn),空節(jié)點(diǎn))是黑色的。

性質(zhì)4:每個(gè)紅色節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色。(從每個(gè)葉子到根的所有路徑上不能有兩個(gè)連續(xù)的紅色節(jié)點(diǎn))。

性質(zhì)5:從任一節(jié)點(diǎn)到其每個(gè)葉子的所有路徑都包含相同數(shù)目的黑色節(jié)點(diǎn)。

詳細(xì)了解請點(diǎn)擊。

LinkedHashMap:

HashMap是無序的,只要不涉及線程安全問題,Map基本都可以使用HashMap。如果我們期待一個(gè)有序的Map,這個(gè)時(shí)候,LinkedHashMap就派上用場了,它雖然增加了時(shí)間和空間上的開銷,但是通過維護(hù)一個(gè)運(yùn)行于所有條目的雙向鏈表,LinkedHashMap保證了元素迭代的順序,該迭代順序可以是插入順序或者是訪問順序。那么是如何維護(hù)的呢,首先參考HashMap的存儲結(jié)構(gòu),將其中的Entry元素增加一個(gè)pre指針和一個(gè)next指針,這樣,根據(jù)插入元素的順序?qū)⒏鱾€(gè)元素依次連接起來,這樣LinkedHashMap就保證了元素的順序。

繼承自HashMap,實(shí)現(xiàn)了Map接口,LinkedHashMap重寫了父類HashMap的get方法,實(shí)際在調(diào)用父類getEntry()方法取得查找的元素后,再判斷當(dāng)排序模式accessOrder為true時(shí)(即按訪問順序排序),先將當(dāng)前節(jié)點(diǎn)從鏈表中移除,然后再將當(dāng)前節(jié)點(diǎn)插入到鏈表尾部。

實(shí)現(xiàn)LRU緩存:

LinkedHashMap和HashMap+LinkedList的操作都是類似的,LRU緩存是我最近看到一個(gè)很巧妙的東西,所以推薦大家看一下這篇文章。

對比下Hashmap、Hashtable和ConcurrentHashmap:

第一、Hashmap是線程不安全的,Hashtable和ConcurrentHashMap是線程安全的,在Hashtable中使用了關(guān)鍵字synchronized修飾,加上了同步鎖;ConcurrentHashMap在JDK1.7中采用了鎖分離的技術(shù),每一個(gè)Segment都獨(dú)立上鎖,保證了并發(fā)的安全性;每一個(gè)Segment元素存儲的是HashEntry數(shù)組+ 鏈表,Segment的大小是一開始就確定的,后期不能再進(jìn)行擴(kuò)容,但是單個(gè)Segment里面的數(shù)組是可以擴(kuò)容的。

但是在JDK1.8上則摒棄了Segment的概念,而是直接用Node數(shù)組+鏈表+紅黑樹的數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn),如下圖所示,并發(fā)控制使用Synchronized和CAS來操作,每一個(gè)Node節(jié)點(diǎn)都是用volatile修飾的,整個(gè)看起來就像是優(yōu)化過且線程安全的HashMap。

第二、Hashmap是可以存放key和value均為null的,存放在table[0]的位置,此時(shí)使用put方法在添加元素的時(shí)候,如果在table[0]中已經(jīng)存入key為null的元素則給null賦上新的value值并返回后面的值,否則則初始化null的元素,存入put里面存放的值。Hashtable和ConcurrentHashMap是不可以存放null的key或者value的,原因和并發(fā)狀態(tài)下的操作有關(guān),當(dāng)在并發(fā)狀態(tài)下執(zhí)行無法分辨是key沒找到的null還是有key值為null,這在多線程里面是模糊不清的,所以不允許put、get為null的元素,如果強(qiáng)行操作就會報(bào)空指針異常。

總結(jié)

本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • Spring+Quartz配置定時(shí)任務(wù)實(shí)現(xiàn)代碼

    Spring+Quartz配置定時(shí)任務(wù)實(shí)現(xiàn)代碼

    這篇文章主要介紹了Spring+Quartz配置定時(shí)任務(wù)實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Spring實(shí)戰(zhàn)之ServletContextResource訪問資源文件示例

    Spring實(shí)戰(zhàn)之ServletContextResource訪問資源文件示例

    這篇文章主要介紹了Spring實(shí)戰(zhàn)之ServletContextResource訪問資源文件,結(jié)合實(shí)例形式分析了spring使用ServletContextResource讀取與遍歷資源文件相關(guān)操作技巧,需要的朋友可以參考下
    2019-12-12
  • java synchronized加載加鎖-線程可重入詳解及實(shí)例代碼

    java synchronized加載加鎖-線程可重入詳解及實(shí)例代碼

    這篇文章主要介紹了java synchronized加載加鎖-線程可重入詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Mybatis中Mapper標(biāo)簽總結(jié)大全

    Mybatis中Mapper標(biāo)簽總結(jié)大全

    這篇文章主要介紹了Mybatis中Mapper標(biāo)簽總結(jié)大全,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • SpringBoot3利用AOP實(shí)現(xiàn)IP黑名單功能

    SpringBoot3利用AOP實(shí)現(xiàn)IP黑名單功能

    在Web應(yīng)用開發(fā)中,控制對特定IP地址的訪問權(quán)限是一個(gè)常見的需求,通過實(shí)現(xiàn)IP黑白名單功能,我們可以允許某些IP地址訪問應(yīng)用,同時(shí)拒絕其他IP地址的訪問,本文將詳細(xì)介紹SpringBoot3利用AOP實(shí)現(xiàn)IP黑名單功能,并附上相應(yīng)的代碼片段,需要的朋友可以參考下
    2024-09-09
  • Java單例模式的8種寫法(推薦)

    Java單例模式的8種寫法(推薦)

    這篇文章主要介紹了Java單例模式的8種寫法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • mybatis typeAliases 給實(shí)體類起別名的方法

    mybatis typeAliases 給實(shí)體類起別名的方法

    這篇文章主要介紹了mybatis typeAliases 給實(shí)體類起別名,本文給大家分享兩種用法,通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 深入理解java線程通信

    深入理解java線程通信

    開發(fā)中不免會遇到需要所有子線程執(zhí)行完畢通知主線程處理某些邏輯的場景。或者是線程 A 在執(zhí)行到某個(gè)條件通知線程 B 執(zhí)行某個(gè)操作。下面我們來一起學(xué)習(xí)如何解決吧
    2019-05-05
  • Java實(shí)現(xiàn)聯(lián)系人管理系統(tǒng)

    Java實(shí)現(xiàn)聯(lián)系人管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)聯(lián)系人管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 一篇文章帶你了解JavaSE的數(shù)據(jù)類型

    一篇文章帶你了解JavaSE的數(shù)據(jù)類型

    這篇文章主要給大家介紹了關(guān)于JavaSE的數(shù)據(jù)類型,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-09-09

最新評論