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

Java初學(xué)者必會(huì)的Map集合及其原理

 更新時(shí)間:2023年06月14日 09:21:47   作者:一一哥Sun  
這篇文章主要給大家介紹Map集合及其原理,該集合中的信息是key-value形式,Map集合與Collection集合又有什么不同呢,要想搞清楚以上問題,下面跟著小編一起來看看吧

一. Map集合

1. 簡介

Map集合是一種以鍵值對(duì)形式存儲(chǔ)和操作數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),建立了key-value之間的映射關(guān)系,常用于存儲(chǔ)和處理復(fù)雜的數(shù)據(jù)。同時(shí)Map也是一種雙列集合接口,它有多個(gè)實(shí)現(xiàn)類,包括HashMap、TreeMap、LinkedHashMap等,最常用的是HashMap類。其中,HashMap是按哈希算法來實(shí)現(xiàn)存取鍵對(duì)象的,這是我們開發(fā)時(shí)最常用的Map子類,而TreeMap類則可以對(duì)鍵對(duì)象進(jìn)行排序。

Map集合中的每個(gè)元素,都包含了一個(gè)鍵(key)和一個(gè)值(value),key和value組成了鍵-值的映射表,我們稱其為鍵值對(duì)。鍵用于唯一標(biāo)識(shí)一個(gè)元素,值用于存儲(chǔ)該元素的數(shù)據(jù),一般情況下,這個(gè)key和value可以是任何引用類型的數(shù)據(jù)。其中,鍵key是無序、無下標(biāo)、不重復(fù)的,最多只能有一個(gè)key為null。值value是無序、無下標(biāo)、可重復(fù)的,可以有多個(gè)value為null

并且這個(gè)key和value之間是單向的一對(duì)一關(guān)系,即通過指定的key,總能找到唯一的、確定的value。當(dāng)我們想從Map中取出數(shù)據(jù)時(shí),只要給出指定的key,就能取出對(duì)應(yīng)的value。

2. 特點(diǎn)

根據(jù)上面我們對(duì)Map概念的講解,把Map的主要特點(diǎn)給大家總結(jié)如下:

  • Map List 不同,Map是一種雙列集合;
  • Map 存儲(chǔ)的是 key-value 的映射關(guān)系;
  • Map 不保證順序 。在遍歷時(shí),遍歷的順序不一定是 put() 時(shí)放入的 key 的順序,也不一定是 key 的排序順序。

3. 實(shí)現(xiàn)方式

在Java中,Map集合的實(shí)現(xiàn)方式主要有兩種:基于哈希表和基于樹結(jié)構(gòu)。接下來給大家簡單介紹一下基于這兩種結(jié)構(gòu)的Map集合。

3.1 基于哈希表的Map集合

基于哈希表的Map,其底層是基于哈希表作為數(shù)據(jù)結(jié)構(gòu)的集合,主要的實(shí)現(xiàn)類是HashMap。在HashMap中,每個(gè)元素都包含一個(gè)鍵和一個(gè)值。當(dāng)我們在添加元素時(shí),HashMap會(huì)根據(jù)鍵的哈希值計(jì)算出對(duì)應(yīng)的桶(Bucket),并將元素存儲(chǔ)到該桶中。如果不同的鍵具有相同的哈希值,就會(huì)出現(xiàn)哈希沖突,此時(shí)HashMap會(huì)使用鏈表或紅黑樹等數(shù)據(jù)結(jié)構(gòu)來解決沖突?;诠1淼腗ap集合具有快速的添加、刪除和查找元素的特點(diǎn),但元素的順序是不確定的。

3.2 基于樹結(jié)構(gòu)的Map集合

基于樹結(jié)構(gòu)的Map集合,其底層是基于紅黑樹作為數(shù)據(jù)結(jié)構(gòu)的集合,主要的實(shí)現(xiàn)類是TreeMap。在TreeMap中,每個(gè)元素也都包含一個(gè)鍵和一個(gè)值。我們在添加元素時(shí),TreeMap會(huì)根據(jù)鍵的比較結(jié)果,將元素存儲(chǔ)到正確的位置上,使得元素可以按照鍵的升序或降序排列?;跇浣Y(jié)構(gòu)的Map集合,具有快速查找和遍歷元素的特點(diǎn),但添加和刪除元素的速度較慢。

4. 常用方法

Map集合的使用和其他集合類似,主要包括添加、刪除、獲取、遍歷元素等操作。當(dāng)我們調(diào)用put(K key, V value)方法時(shí),會(huì)把key和value進(jìn)行映射并放入Map。當(dāng)調(diào)用V get(K key)時(shí),可以通過key獲取到對(duì)應(yīng)的value;如果key不存在,則返回null。如果我們只是想查詢某個(gè)key是否存在,可以調(diào)用containsKey(K key)方法。另外我們也可以通過 Map提供的keySet()方法,獲取所有key組成的集合,進(jìn)而遍歷Map中所有的key-value對(duì)。

下表中就是Map里的一些常用方法,大家可以記一下,這些方法在Map的各實(shí)現(xiàn)子類中也都存在。

方法描述
clear()刪除Map中所有的鍵-值對(duì)
isEmpty()判斷Map是否為空
size()計(jì)算Map中鍵/值對(duì)的數(shù)量
put()將鍵/值對(duì)添加到Map中
putAll()將所有的鍵/值對(duì)添加到Map中
putIfAbsent()如果Map中不存在指定的鍵,則將指定的鍵/值對(duì)插入到Map中。
remove()刪除Map中指定鍵key的映射關(guān)系
containsKey()檢查Map中是否存在指定key對(duì)應(yīng)的映射關(guān)系。
containsValue()檢查Map中是否存在指定的 value對(duì)應(yīng)的映射關(guān)系。
replace()替換Map中指定key對(duì)應(yīng)的value。
replaceAll()將Map中所有的映射關(guān)系替換成給定函數(shù)執(zhí)行的結(jié)果。
get()獲取指定 key 對(duì)應(yīng)對(duì) value
getOrDefault()獲取指定 key 對(duì)應(yīng)對(duì) value,如果找不到 key ,則返回設(shè)置的默認(rèn)值
forEach()對(duì)Map中的每個(gè)映射執(zhí)行指定的操作。
entrySet()返回Map中所有的鍵值對(duì)
keySet()返回Map中所有的key鍵
values()返回Map中所有的value值
merge()添加鍵值對(duì)到Map中
ompute()對(duì)Map中指定key的值進(jìn)行重新計(jì)算
computeIfAbsent()對(duì)Map中指定key的值進(jìn)行重新計(jì)算,如果該key不存在,則添加到Map中
computeIfPresent()當(dāng)key存在該Map時(shí),對(duì)Map中指定key的值進(jìn)行重新計(jì)算

5. 常用實(shí)現(xiàn)類

Java中有多個(gè)Map接口的實(shí)現(xiàn)類,接下來就給大家逐一簡單介紹一下。

5.1 HashMap

HashMap是Java中最常用的Map集合實(shí)現(xiàn)類,它基于哈希表實(shí)現(xiàn),具有快速查找鍵值對(duì)的優(yōu)點(diǎn)。 HashMap的存儲(chǔ)方式是無序的,也就是說,遍歷HashMap集合時(shí),得到的鍵值對(duì)順序是不確定的。下面是創(chuàng)建HashMap集合的代碼示例:

Map<String, Integer> hashMap = new HashMap<>();

5.2 TreeMap

TreeMap是Java中另一個(gè)常用的Map集合實(shí)現(xiàn)類,它基于紅黑樹實(shí)現(xiàn),具有自動(dòng)排序鍵值對(duì)的優(yōu)點(diǎn)。TreeMap的存儲(chǔ)方式是有序的,也就是說,遍歷TreeMap集合時(shí)得到的鍵值對(duì),是按照鍵的自然順序或指定比較器的順序排序的。下面是創(chuàng)建TreeMap集合的代碼示例:

Map<String, Integer> treeMap = new TreeMap<>();

5.3 LinkedHashMap

LinkedHashMap是Java中另一個(gè)Map集合實(shí)現(xiàn)類,它繼承自HashMap,并保持了插入順序。也就是說,遍歷LinkedHashMap集合時(shí),得到的鍵值對(duì)的順序是按照插入順序排序的。下面是創(chuàng)建LinkedHashMap集合的代碼示例:

Map<String, Integer> linkedHashMap = new LinkedHashMap<>();

5.4 Hashtable

Hashtable是Java中另一個(gè)Map集合實(shí)現(xiàn)類,它與HashMap非常相似,但Hashtable是線程安全的。Hashtable的存儲(chǔ)方式是無序的,也就是說,遍歷Hashtable集合時(shí),得到的鍵值對(duì)的順序是不確定的。下面是創(chuàng)建Hashtable集合的代碼示例:

Map<String, Integer> hashtable = new Hashtable<>();

需要注意的是,由于Hashtable是線程安全的,所以在多線程環(huán)境中使用Hashtable的性能可能會(huì)較低,現(xiàn)在一般是建議使用ConcurrentHashMap。

5.5 ConcurrentHashMap

ConcurrentHashMap是Java中的另一個(gè)Map集合實(shí)現(xiàn)類,它與Hashtable非常相似,但是ConcurrentHashMap是線程安全的,并且性能更高。ConcurrentHashMap的存儲(chǔ)方式是無序的,也就是說,遍歷ConcurrentHashMap集合時(shí),得到的鍵值對(duì)的順序是不確定的。下面是創(chuàng)建ConcurrentHashMap集合的代碼示例:

Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();

需要注意的是,雖然ConcurrentHashMap是線程安全的,但仍然需要注意多線程環(huán)境下的并發(fā)問題。

二. HashMap集合

1. 簡介

HashMap繼承自AbstractMap類,實(shí)現(xiàn)了Map、Cloneable、java.io.Serializable接口,如下圖所示:

HashMap是基于散列表實(shí)現(xiàn)的雙列集合,它存儲(chǔ)的是key-value鍵值對(duì)映射,每個(gè)key-value鍵值對(duì)也被稱為一條Entry條目。其中的 key與 value,可以是任意的數(shù)據(jù)類型,其類型可以相同也可以不同。但一般情況下,key都是String類型,有時(shí)候也可以使用Integer類型;value可以是任何類型。并且在HashMap中,最多只能有一個(gè)記錄的key為null,但可以有多個(gè)value的值為null。

HashMap中這些鍵值對(duì)(Entry)會(huì)分散存儲(chǔ)在一個(gè)數(shù)組當(dāng)中,這個(gè)數(shù)組就是HashMap的主體。 默認(rèn)情況下,HashMap這個(gè)數(shù)組中的每個(gè)元素的初始值都是null。但HashMap中最多只允許一條記錄的key為null,允許多條記錄的value為null。

另外HashMap是非線程安全的,即任一時(shí)刻如果有多個(gè)線程同時(shí)對(duì)HashMap進(jìn)行寫操作,有可能會(huì)導(dǎo)致數(shù)據(jù)的不一致。 如果需要滿足線程的安全性要求,可以用 Collections.synchronizedMap() 方法使得HashMap具有線程安全的能力,或者使用ConcurrentHashMap來替代。

HashMap實(shí)現(xiàn)了Map接口,會(huì)根據(jù)key的hashCode值存儲(chǔ)數(shù)據(jù),具有較快的訪問速度。另外HashMap是無序的,不會(huì)記錄插入元素的順序。我們一般是根據(jù)key的hashCode值來存取數(shù)據(jù), 大多數(shù)情況下都可以直接根據(jù)key來定位到它所對(duì)應(yīng)的值,因而HashMap有較快的訪問速度,但遍歷順序卻不確定。

2. 特點(diǎn)

根據(jù)上面的描述,把HashMap的核心特點(diǎn)總結(jié)如下:

  • 基于哈希表實(shí)現(xiàn),具有快速查找鍵值對(duì)的優(yōu)點(diǎn);
  • HashMap的存儲(chǔ)方式是無序的;
  • HashMap的key-value可以是任意類型,但key一般都是String類型,value類型任意;
  • HashMap最多只能有一個(gè)記錄的key為null,但可以有多個(gè)value為null。

3. 常用操作

HashMap的使用方法和其他Map集合類似,主要包括添加元素、刪除元素、獲取元素、遍歷元素等操作。接下來會(huì)詳細(xì)地給大家介紹HashMap集合的常用操作。

3.1 創(chuàng)建Map集合對(duì)象

創(chuàng)建HashMap對(duì)象的方式有多種,我們可以使用構(gòu)造函數(shù),代碼如下:

// 使用構(gòu)造函數(shù)創(chuàng)建HashMap對(duì)象 
Map<String, Integer> map1 = new HashMap<>();

3.2 添加元素

向HashMap集合添加元素的方式和List集合類似,也是使用put()方法,下面是向HashMap集合中添加元素的代碼示例:

/**
 * @author
 */
public class Demo14 {
    public static void main(String[] args) {
        //HashMap
        Map<String, String> map = new HashMap<>();
        map.put("name","一一哥");
        //map.put("name","壹哥");
        map.put("age", "30");
        map.put("sex", "男");
        System.out.println("map="+map);
    }
}

看到上面的代碼,有些小伙伴可能會(huì)問,如果我們往同一個(gè)Map中存儲(chǔ)兩個(gè)相同的key,但分別放入不同的value,這會(huì)有什么問題嗎?

其實(shí)我們在Map中,重復(fù)地放入 key-value 并不會(huì)有任何問題,但一個(gè) key 只能關(guān)聯(lián)一個(gè) value 。 因?yàn)楫?dāng)我們調(diào)用put(K key, V value)方法時(shí),如果放入的key已經(jīng)存在,put()方法會(huì)返回被刪除的舊value,否則就會(huì)返回null。所以Map中不會(huì)有重復(fù)的key,因?yàn)榉湃胂嗤膋ey時(shí),就會(huì)把原有key對(duì)應(yīng)的value給替換掉。

3.3 刪除元素

從HashMap集合中刪除元素的方式也和List集合類似,使用remove()方法。下面是從HashMap集合中刪除元素的代碼示例:

//從HashMap集合中移除元素
map.remove("age");

3.4 獲取元素

從HashMap集合中獲取元素的方式和List集合不同,需要使用鍵來獲取值。HashMap集合提供了兩種方法來獲取值,一種是使用get()方法,另一種是使用getOrDefault()方法。如果指定的鍵不存在,使用get()方法會(huì)返回null,而getOrDefault()方法則會(huì)返回指定的默認(rèn)值。下面是從HashMap集合中獲取元素的代碼示例:

/**
 * @author
 */
public class Demo15 {
    public static void main(String[] args) {
        //HashMap
        Map<String, String> map = new HashMap<>();
        map.put("name","一一哥");
        map.put("age", "30");
        map.put("sex", "男");
        //根據(jù)key獲取指定的元素
        String name = map.get("name");
        String age = map.get("age");
        //根據(jù)key獲取指定的元素,并設(shè)置默認(rèn)值
        String sex = map.getOrDefault("sex","男");
        String height = map.getOrDefault("height","0");
        System.out.println("[name]="+name+",[age]="+age+",[sex]="+sex+",[height]="+height);
    }
}

3.5 遍歷元素

遍歷HashMap集合的方式和List集合不同,需要使用迭代器或者foreach循環(huán)遍歷鍵值對(duì),下面是遍歷HashMap集合的代碼示例:

/**
 * @author
 */
public class Demo16 {
    public static void main(String[] args) {
        //HashMap
        Map<String, String> map = new HashMap<>();
        map.put("name","一一哥");
        map.put("age", "30");
        map.put("sex", "男");
        //遍歷方式一:使用迭代器遍歷HashMap 
        //獲取集合中的entry條目集合
        Set<Entry<String, String>> entrySet = map.entrySet();
        //獲取集合中攜帶的Iterator迭代器對(duì)象
        Iterator<Map.Entry<String, String>> iterator = entrySet.iterator(); 
        //通過循環(huán)進(jìn)行迭代遍歷
        while (iterator.hasNext()) {     
            //獲取每一個(gè)Entry條目對(duì)象
            Map.Entry<String, String> entry = iterator.next();    
            //獲取條目中的key
            String key = entry.getKey();    
            //獲取條目中的value
            String value = entry.getValue();     
            System.out.println(key + " = " + value); 
        } 
        //遍歷方式二:用foreach循環(huán)遍歷HashMap 
        for (Map.Entry<String, String> entry : map.entrySet()) {    
            String key = entry.getKey();     
            String value = entry.getValue();     
            System.out.println(key + " = " + value); 
        }
    }
}

大家要注意,當(dāng)我們使用Map時(shí),任何依賴順序的邏輯都是不可靠的。比如,我們存入"A","B","C" 3個(gè)key,遍歷時(shí),每個(gè)key會(huì)保證被遍歷一次且僅遍歷一次,但遍歷的順序完全沒有保證,甚至對(duì)于不同的JDK版本,相同的代碼遍歷輸出的順序都可能是不同的!所以我們在 遍歷Map時(shí),要注意輸出的key是無序的!

3.6 判斷Map集合是否為空

判斷HashMap集合是否為空可以使用isEmpty()方法,如果Map集合為空,則返回true,否則返回false。下面是判斷HashMap集合是否為空的代碼示例:

// 判斷HashMap是否為空 
boolean isEmpty = map.isEmpty(); 

3.7 獲取Map集合的大小

獲取HashMap集合的大小可以使用size()方法,返回HashMap集合中鍵值對(duì)的數(shù)量,下面是獲取HashMap集合大小的代碼示例:

// 獲取HashMap的大小 
int size = map.size(); 

3.8 判斷Map集合是否包含指定的鍵或值

如果我們想判斷HashMap集合是否包含指定的鍵或值,可以使用containsKey()和containsValue()方法。如果Map集合包含指定的鍵或值,則返回true,否則返回false。下面是判斷HashMap集合是否包含指定鍵或值的代碼示例:

// 判斷HashMap是否包含指定鍵
boolean containsKey = map.containsKey("name"); 
// 判斷HashMap是否包含指定值 
boolean containsValue = map.containsValue("一一哥"); 

3.9 替換Map集合中的鍵值對(duì)

如果我們想替換HashMap集合中的鍵值對(duì),可以使用replace()方法將指定鍵的值替換成新的值。如果指定的鍵不存在,則不進(jìn)行任何操作。下面是替換HashMap集合中的鍵值對(duì)的代碼示例:

// 替換HashMap中的鍵值對(duì) 
map.replace("name", "壹哥"); 

3.10 合并兩個(gè)Map集合

如果我們想合并兩個(gè)HashMap集合,可以使用putAll()方法,將另一個(gè)HashMap集合中所有的鍵值對(duì),添加到當(dāng)前的HashMap集合中。下面是合并兩個(gè)HashMap集合的代碼示例:

/**
 * @author
 */
public class Demo16 {
    public static void main(String[] args) {
        //HashMap
        Map<String, String> map1 = new HashMap<>();
        map1.put("name","一一哥");
        map1.put("age", "30");
        map1.put("sex", "男");
        // 創(chuàng)建另一個(gè)TreeMap集合 
        Map<String, String> map2 = new HashMap<>(); 
        map2.put("height", "180"); 
        map2.put("salary", "5w"); 
        //將map1中的鍵值對(duì)添加到map2中 
        map2.putAll(map1); 
        System.out.println("map2="+map2);
    }
}

3.11 獲取Map集合中所有的鍵或值

如果我們想獲取HashMap集合中所有的鍵或值,可以分別使用keySet()和values()方法。這兩個(gè)方法會(huì)返回一個(gè)Set集合和一個(gè)Collection集合,它們包含了HashMap集合中所有的鍵或值。下面是獲取HashMap集合中所有鍵或值的代碼示例:

/**
 * @author 
 */
public class Demo18 {
    public static void main(String[] args) {
        //HashMap
        Map<String, String> map = new HashMap<>();
        map.put("name","一一哥");
        map.put("age", "30");
        map.put("sex", "男");
        // 獲取HashMap中所有的鍵 
        Set<String> keySet = map.keySet(); 
        for(String key : keySet) {
            System.out.println("key="+key); 
        }
        // 獲取HashMap中所有的值 
        Collection<String> values = map.values(); 
        for(String value:values) {
            System.out.println("value"+value); 
        }
    }
}

4. 原理概述(重點(diǎn))

作為開發(fā)時(shí)最常用的Map集合,HashMap在我們面試時(shí)被問到的概率非常高,尤其是關(guān)于其原理、數(shù)據(jù)結(jié)構(gòu)、沖突解決、并發(fā)、擴(kuò)容等相關(guān)的內(nèi)容,更是經(jīng)常被問到。

如果我們想要了解HashMap的底層原理,首先得知道HashMap的底層數(shù)據(jù)結(jié)構(gòu),而這個(gè)數(shù)據(jù)結(jié)構(gòu)在不同的JDK版本中是不同的。我們可以把HashMap的底層數(shù)據(jù)結(jié)構(gòu)分為兩大版本,即JDK 7及其以前的版本 和 JDK 8及其以后的版本。 大家注意,本文主要是結(jié)合JDK 8的源碼,給大家講解HashMap的底層原理。

  • 在JDK 7及其以前版本的HashMap中,其底層的數(shù)據(jù)結(jié)構(gòu)是 數(shù)組+鏈表 ;
  • 而從JDK 8開始則采用 數(shù)組+鏈表+紅黑樹 的數(shù)據(jù)結(jié)構(gòu),其中的 數(shù)組是Entry類型或者說是Node類型數(shù)組 。

5. hash沖突解決機(jī)制

因?yàn)镠ashMap底層會(huì)使用Hash算法來處理數(shù)據(jù)的存取,當(dāng)數(shù)據(jù)非常多時(shí)就有一定的概率出現(xiàn)hash沖突,其解決過程如下。

  • 當(dāng)我們往HashMap中存儲(chǔ)數(shù)據(jù)時(shí),首先會(huì)利用hash(key)方法 計(jì)算出key的hash值,再利用該 hash值HashMap數(shù)組的長度-1 進(jìn)行 與運(yùn)算,從而得到該key在數(shù)組中對(duì)應(yīng)的下標(biāo)位置
  • 如果該位置上目前還沒有存儲(chǔ)數(shù)據(jù),則直接將該key-value鍵值對(duì)數(shù)據(jù)存入到數(shù)組中的這個(gè)位置;
  • 如果該位置目前已經(jīng)有了數(shù)據(jù),則把新的數(shù)據(jù)存入到一個(gè)鏈表中;
  • 當(dāng)鏈表的長度超過閾值(JDK 8中該值為8)時(shí),會(huì)將鏈表轉(zhuǎn)換為紅黑樹(轉(zhuǎn)換為紅黑樹還需要滿足其他的條件,鏈表長度達(dá)到閾值只是其中的一個(gè)條件)。

通過這樣的機(jī)制,HashMap就解決了存值時(shí)可能產(chǎn)生的哈希沖突問題,并可以大大提高我們的查找效率。

當(dāng)然由于HashMap極其重要,它的內(nèi)容非常多,尤其是原理性的內(nèi)容更多。但由于篇幅限制,不會(huì)在本文中過多地講解HashMap原理等的內(nèi)容。

三. Hashtable集合

1. 簡介

Hashtable也是Java中的一個(gè)Map集合,它與HashMap非常相似,但Hashtable是線程安全的,而HashMap不是線程安全的。Hashtable也可以存儲(chǔ)鍵值對(duì),并可以通過鍵快速查找到對(duì)應(yīng)的值,Hashtable的鍵和值也都可以是任何類型的對(duì)象。

因?yàn)镠ashtable是線程安全的,因此適用于多線程環(huán)境。在多線程環(huán)境中,如果需要對(duì)Hashtable進(jìn)行多個(gè)操作,需要使用synchronized關(guān)鍵字來保證線程安全。但需要我們注意的是,在多線程環(huán)境中使用Hashtable可能會(huì)影響性能,所以如果不需要保證線程安全,請盡量使用HashMap。

Hashtable集合的底層結(jié)構(gòu)主要是數(shù)組+鏈表,數(shù)組是Entry數(shù)組,鏈表也是用Entry來實(shí)現(xiàn)的 。 所以Hashtable的底層核心,其實(shí)也是基于哈希表,它使用哈希函數(shù)將鍵映射到哈希表中的一個(gè)位置,從而實(shí)現(xiàn)快速查找。另外Hashtable的存儲(chǔ)方式是無序的,也就是說,遍歷Hashtable集合得到的鍵值對(duì)的順序是不確定的。

2. 常用方法

Hashtable與HashMap類似,它的常用方法也與HashMap一樣:

  • put(K key, V value):將指定的鍵值對(duì)存儲(chǔ)到Hashtable中。
  • get(Object key):返回指定鍵所對(duì)應(yīng)的值,如果不存在該鍵則返回null。
  • remove(Object key):從Hashtable中移除指定鍵所對(duì)應(yīng)的鍵值對(duì)。
  • containsKey(Object key):判斷Hashtable中是否包含指定的鍵。
  • containsValue(Object value):判斷Hashtable中是否包含指定的值。
  • size():返回Hashtable中鍵值對(duì)的數(shù)量。
  • clear():移除Hashtable中的所有鍵值對(duì)。

3. 基本使用

下面是一個(gè)簡單的Hashtable集合示例,演示了如何創(chuàng)建Hashtable集合、存儲(chǔ)鍵值對(duì)、獲取值、遍歷集合等操作。

import java.util.Hashtable;
import java.util.Map;
/**
 * @author
 */
public class Demo19 {
    public static void main(String[] args) {
        // 創(chuàng)建Hashtable集合
        Map<String, Integer> hashtable = new Hashtable<>();
        // 存儲(chǔ)鍵值對(duì)
        hashtable.put("apple", 10);
        hashtable.put("banana", 20);
        hashtable.put("orange", 30);
        // 獲取值
        int value1 = hashtable.get("apple");
        int value2 = hashtable.get("banana");
        int value3 = hashtable.get("orange");
        System.out.println("apple: " + value1);
        System.out.println("banana: " + value2);
        System.out.println("orange: " + value3);
        // 移除鍵值對(duì)
        hashtable.remove("orange");
        // 遍歷集合
        for (Map.Entry<String, Integer> entry : hashtable.entrySet()) {
            String key = entry.getKey();
            int value = entry.getValue();
            System.out.println(key + ": " + value);
        }
        // 清空集合
        hashtable.clear(); 
    }
}

其他方法的使用與HashMap基本一致,就不再一一細(xì)說了。

四. ConcurrentHashMap集合

1. 簡介

由于在多線程環(huán)境下,常規(guī)的HashMap可能會(huì)在數(shù)組擴(kuò)容及重哈希時(shí)出現(xiàn) 死循環(huán)、臟讀 等線程安全問題,雖然有HashTable、Collections.synchronizedMap()可以取代HashMap進(jìn)行并發(fā)操作,但因它們都是利用一個(gè) 全局的synchronized鎖 來同步不同線程之間的并發(fā)訪問,因此性能較差。

所以Java就從JDK 1.5版本開始,在J.U.C(java.util.concurrent并發(fā)包) 引入了一個(gè)高性能的并發(fā)操作集合---ConcurrentHashMap(可以簡稱為CHM)。 集合是一種線程安全的哈希表實(shí)現(xiàn),相比Hashtable和SynchronizedMap,在多線程場景下具有更好的性能和可伸縮性

并且ConcurrentHashMap集合在讀數(shù)據(jù)時(shí)不需要加鎖,寫數(shù)據(jù)時(shí)會(huì)加鎖,但鎖的粒度較小,不會(huì)對(duì)整個(gè)集合加鎖。而其內(nèi)部又大量的利用了 volatile,final,CAS等lock-free(無鎖并發(fā))技術(shù),減少了鎖競爭對(duì)于性能的影響,具有更好的寫并發(fā)能力,但降低了對(duì)讀一致性的要求。 因此既保證了并發(fā)操作的安全性,又確保了讀、寫操作的高性能,可以說它的并發(fā)設(shè)計(jì)與實(shí)現(xiàn)都非常的精巧。

另外ConurrentHashMap中的key與value都不能為null,否則會(huì)產(chǎn)生空指針異常!

2. ConcurrentHashMap類關(guān)系

ConcurrentHashMap與HashMap具有相同的父類AbstractMap,他倆可以說是“親兄弟”,所以ConcurrentHashMap在一般的特性和使用上與HashMap基本是一致的,甚至很多底層原理也是相似的。

但兩者所在的包是不同的,ConcurrentHashMap是在java.util.concurrent包中,HashMap是在java.util包中,我們可以參考下圖:

以上所述的ConcurrentHashMap概念及特征,是不區(qū)分版本的,但實(shí)際上不同版本的ConcurrentHashMap內(nèi)部實(shí)現(xiàn)差異很大,所以面試時(shí)經(jīng)常會(huì)被問到不同版本之間的差異、各自特征。接下來會(huì)針對(duì)JDK 7 與 JDK 8 這兩個(gè)經(jīng)典版本分別進(jìn)行闡述。

3. 實(shí)現(xiàn)原理

3.1 并發(fā)控制機(jī)制

在JDK 7中,ConcurrentHashMap的核心機(jī)制是分段鎖(Segment),每個(gè)Segment內(nèi)部維護(hù)了一個(gè)哈希表,且這個(gè)哈希表是線程安全的。而ConcurrentHashMap中的每個(gè)操作,都是先對(duì)所在的Segment進(jìn)行加鎖,然后再執(zhí)行具體的操作。

當(dāng)多個(gè)線程對(duì)不同的Segment進(jìn)行操作時(shí),它們之間是并發(fā)的。當(dāng)多個(gè)線程對(duì)同一個(gè)Segment進(jìn)行操作時(shí),它們會(huì)競爭鎖,但不會(huì)影響到其他Segment的操作。這種機(jī)制有效地降低了鎖的粒度,提高了并發(fā)訪問效率。

ConcurrentHashMap的另一個(gè)優(yōu)點(diǎn)是支持可伸縮性。當(dāng)需要增加ConcurrentHashMap的容量時(shí),我們只需要增加Segment的數(shù)量即可,這種機(jī)制使得ConcurrentHashMap在高并發(fā)場景下具有良好的可伸縮性。

3.2 JDK 7版本的ConcurrentHashMap

在JDK 7版本中,ConcurrentHashMap和HashMap的設(shè)計(jì)思路其實(shí)是差不多的,但為了支持并發(fā)操作,做了一定的改進(jìn)。比如ConcurrentHashMap中的數(shù)據(jù)是一段一段存放的,我們把這些分段稱之為Segment分段,在每個(gè)Segment分段中又有 數(shù)組+鏈表 的數(shù)據(jù)結(jié)構(gòu)。默認(rèn)情況下ConcurrentHashMap把主干分成了 16個(gè)Segment分段,并對(duì)每一段都單獨(dú)加鎖,我們把這種設(shè)計(jì)策略稱之為 "分段鎖", ****ConcurrentHashMap就是利用這種分段鎖機(jī)制進(jìn)行并發(fā)控制的。JDK 7中ConcurrentHashMap的基本數(shù)據(jù)結(jié)構(gòu)如下圖所示:

在理想狀態(tài)下,ConcurrentHashMap可以 同時(shí)支持16個(gè)線程 執(zhí)行并發(fā)寫操作,以及任意數(shù)量的線程進(jìn)行并發(fā)讀操作。在寫操作時(shí),通過分段鎖技術(shù),只對(duì)所操作的段加鎖而不會(huì)影響其它分段,且在讀操作時(shí)(幾乎)不需要加鎖。

3.3 JDK 8版本的ConcurrentHashMap

在JDK 8中,ConcurrentHashMap相對(duì)于JDK 7版本做了很大的改動(dòng),從實(shí)現(xiàn)的代碼量上就可以看出來,JDK 7中ConcurrentHashMap不到2000行代碼,而JDK 8中則有6000多行代碼。

JDK 8中放棄了臃腫的Segment設(shè)計(jì),取而代之采用了 Node數(shù)組 + 鏈表 + 紅黑樹 + CAS + Synchronized 技術(shù)來保證并發(fā)安全,實(shí)現(xiàn)并發(fā)控制操作。但是在ConcurrentHashMap的實(shí)現(xiàn)中保留了 Segment 的定義,這是為了 保證序列化時(shí)的兼容性,但并沒有任何結(jié)構(gòu)上的用處。在JDK 8中用synchronized替換ReentrantLock的原因大致如下:

  • 減少內(nèi)存開銷: 如果使用ReentrantLock,就需要節(jié)點(diǎn)繼承AQS來獲得同步支持,這增加了內(nèi)存開銷,而JDK 8中只有頭節(jié)點(diǎn)需要進(jìn)行同步。
  • 內(nèi)部優(yōu)化: synchronized是JVM直接支持的,JVM能夠在運(yùn)行時(shí)作出相應(yīng)的優(yōu)化措施,比如 鎖粗化、鎖消除、鎖自旋等。

4. 基本使用

ConcurrentHashMap支持與HashMap相同的常用操作,如put、get、remove等。下面介紹一些常用操作。

4.1 插入元素

ConcurrentHashMap的put方法用于向集合中插入一個(gè)鍵值對(duì),如果鍵已經(jīng)存在,則會(huì)更新對(duì)應(yīng)的值。下面是向ConcurrentHashMap中插入元素的示例:

import java.util.concurrent.ConcurrentHashMap;
public class Demo19 {
    public static void main(String[] args) {
        // 創(chuàng)建ConcurrentHashMap集合
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        // 插入元素
        map.put("apple", 10);
        map.put("banana", 20);
        map.put("orange", 30);
        // 輸出元素
        System.out.println(map);
    }
}

根據(jù)上面代碼的執(zhí)行結(jié)果可知,ConcurrentHashMap中的鍵值對(duì)是無序的。

4.2 獲取元素

ConcurrentHashMap的get方法用于獲取指定鍵對(duì)應(yīng)的值,如果鍵不存在,則返回null。下面是一個(gè)從ConcurrentHashMap中獲取元素的示例:

import java.util.concurrent.ConcurrentHashMap;
public class Demo20 {
    public static void main(String[] args) {
        // 創(chuàng)建ConcurrentHashMap集合
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        // 插入元素
        map.put("apple", 10);
        map.put("banana", 20);
        map.put("orange", 30);
        // 獲取元素
        Integer value = map.get("apple");
        System.out.println(value);
    }
}

4.3 刪除元素

ConcurrentHashMap的remove方法用于從集合中刪除指定鍵的元素,下面是一個(gè)從ConcurrentHashMap中刪除元素的示例:

import java.util.concurrent.ConcurrentHashMap;
public class Demo21 {
    public static void main(String[] args) {
        // 創(chuàng)建ConcurrentHashMap集合
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        // 插入元素
        map.put("apple", 10);
        map.put("banana", 20);
        map.put("orange", 30);
        // 刪除元素
        map.remove("apple");
        // 輸出元素
        System.out.println(map);
    }
}

五. 結(jié)語

至此,我們就把Map及其子類學(xué)習(xí)完了,現(xiàn)在你知道Map有什么特性及其用法了嗎?接下來我們簡單總結(jié)一下今天的重點(diǎn)吧:

  • Map是一種映射表,可以通過key快速查找value;
  • 可以通過 for each 遍歷 keySet() ,也可以通過 for each 遍歷 entrySet() ,直接獲取 key-value ;
  • 最常用的Map是HashMap;
  • Hashtable是線程安全的集合,底層結(jié)構(gòu)主要是數(shù)組+鏈表;
  • ConcurrentHashMap可以良好地進(jìn)行并發(fā)處理,是一種線程安全且高效的集合。

以上就是Java初學(xué)者必會(huì)的Map集合及其原理的詳細(xì)內(nèi)容,更多關(guān)于JavaMap集合及原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Springboot自動(dòng)裝配實(shí)現(xiàn)過程代碼實(shí)例

    Springboot自動(dòng)裝配實(shí)現(xiàn)過程代碼實(shí)例

    這篇文章主要介紹了Springboot自動(dòng)裝配實(shí)現(xiàn)過程代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • 必須掌握的十個(gè)Lambda表達(dá)式簡化代碼提高生產(chǎn)力

    必須掌握的十個(gè)Lambda表達(dá)式簡化代碼提高生產(chǎn)力

    這篇文章主要為大家介紹了必須掌握的十個(gè)Lambda表達(dá)式來簡化代碼提高生產(chǎn)力,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • springboot 按月分表的實(shí)現(xiàn)方式

    springboot 按月分表的實(shí)現(xiàn)方式

    本文主要介紹了springboot 按月分表的實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Java定時(shí)器例子_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java定時(shí)器例子_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    本文給大家分享了java定時(shí)器例子,非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下吧
    2017-05-05
  • Java mybatis-plus詳解

    Java mybatis-plus詳解

    MyBatis-Plus是一個(gè)MyBatis的增強(qiáng)工具,在MyBatis的基礎(chǔ)上只做增強(qiáng)不做修改,為簡化開發(fā)、提高效率而生,本文給大家詳細(xì)講解一下MyBatis-Plus,需要的朋友參考下吧
    2021-09-09
  • JDK源碼白話解讀之ThreadLocal篇

    JDK源碼白話解讀之ThreadLocal篇

    其實(shí)網(wǎng)上有很多關(guān)于ThreadLocal的文章了,有不少文章也已經(jīng)寫的非常好了。但是很多同學(xué)反應(yīng)還有一些部分沒有講解的十分清楚,還是有一定的疑惑沒有想的十分清楚
    2022-02-02
  • Java實(shí)現(xiàn)分頁查詢功能

    Java實(shí)現(xiàn)分頁查詢功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)分頁查詢功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 詳解Java的MyBatis框架和Spring框架的整合運(yùn)用

    詳解Java的MyBatis框架和Spring框架的整合運(yùn)用

    在Web端的SSH框架整合中Spring主要負(fù)責(zé)數(shù)據(jù)庫處理,而引入MyBatis后二者的集成使用效果更佳,下面我們就來詳解Java的MyBatis框架和Spring框架的整合運(yùn)用
    2016-06-06
  • MyBatis-Plus中的邏輯刪除功能及實(shí)例分析

    MyBatis-Plus中的邏輯刪除功能及實(shí)例分析

    本文將詳細(xì)講解MyBatis-Plus中的邏輯刪除特性,并結(jié)合實(shí)際案例進(jìn)行演示和說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Spring Shell打Jar包時(shí)常用小技巧

    Spring Shell打Jar包時(shí)常用小技巧

    這篇文章主要介紹了Spring Shell打Jar包時(shí)常用小技巧,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10

最新評(píng)論