一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java中詳細解析Map接口

Java中詳細解析Map接口

2021-11-13 12:19IT劉華強 Java教程

這篇文章主要介紹了Java8 中 Map 接口的新方法,本文通過代碼實例給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下

 

Map詳解:

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

Java中詳細解析Map接口

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

  • 給定一個鍵和一個值,你可以將該值存儲在一個Map對象. 之后,你可以通過鍵來訪問對應的值。
  • 當訪問的值不存在的時候,方法就會拋出一個NoSuchElementException異常.
  • 當對象的類型和Map里元素類型不兼容的時候,就會拋出一個 ClassCastException異常。
  • 當在不允許使用Null對象的Map中使用Null對象,會拋出一個NullPointerException 異常。
  • 當嘗試修改一個只讀的Map時,會拋出一個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是由數組和鏈表這兩個結構來存儲數據。

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

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

hashMap則結合了兩者的優點,既滿足了尋址,又滿足了操作,為什么呢?關鍵在于它的存儲結構。

它底層是一個數組,數組元素就是一個鏈表形式,見下圖:

Java中詳細解析Map接口

Entry: 存儲鍵值對。

Map類在設計時提供了一個靜態修飾接口Entry。Entry將鍵值對的對應關系封裝成了鍵值對對象,這樣我們在遍歷Map集合時,就可以從每一個鍵值對對象中獲取相應的鍵與值。之所以被修飾成靜態是為了可以用類名直接調用。

Java中詳細解析Map接口

每次初始化HashMap都會構造一個table數組,而table數組的元素為Entry節點,它里面包含了鍵key,值value,下一個節點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發現,它有4個構造函數:

Java中詳細解析Map接口

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

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

3、指定初始容量和默認加載因子的空HashMap。

4、構造一個映射關系與指定Map相同的新HashMap。

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

 

Put方法:

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

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

Java中詳細解析Map接口

Java中詳細解析Map接口

上面說到如果數組擴容,那么每次要怎么擴容呢?

Java中詳細解析Map接口

Java中詳細解析Map接口

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

這里需要說明的是,hashmap是可以存放key和value均為null的,存放在table[0]的位置,此時使用put方法在添加元素的時候,如果在table[0]中已經存入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方法:

Java中詳細解析Map接口

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

Remove方法:

判斷,如果hashMap的size是0,返回null;找到需要移除的元素的前一個節點,然后把前驅節點的next指向刪除節點的next節點,此時當前節點沒有任何引用指向,它在程序結束之后就會被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這里可以用增強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);
        // 增強for循環 =========== 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 是一個有序的key-value集合,繼承于AbstractMap,它是通過紅黑樹實現的。TreeMap 實現了NavigableMap接口,實現了Cloneable接口,實現了java.io.Serializable接口。

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

紅黑樹(Red Black Tree) 是一種自平衡二叉查找樹,是在計算機科學中用到的一種數據結構,典型的用途是實現關聯數組。它有五個特點如下:

性質1:節點是紅色或黑色。

性質2:根節點是黑色。

性質3:每個葉節點(NIL節點,空節點)是黑色的。

性質4:每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)。

性質5:從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。

詳細了解請點擊。

 

LinkedHashMap:

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

Java中詳細解析Map接口

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

實現LRU緩存:

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

 

對比下Hashmap、Hashtable和ConcurrentHashmap:

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

Java中詳細解析Map接口

但是在JDK1.8上則摒棄了Segment的概念,而是直接用Node數組+鏈表+紅黑樹的數據結構來實現,如下圖所示,并發控制使用Synchronized和CAS來操作,每一個Node節點都是用volatile修飾的,整個看起來就像是優化過且線程安全的HashMap。

Java中詳細解析Map接口

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

 

總結

本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!

原文鏈接:https://blog.csdn.net/jiguquan3839/article/details/84546835

延伸 · 閱讀

精彩推薦
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
主站蜘蛛池模板: 天天曰| 欧美作爱福利免费观看视频 | 深夜在线观看 | 视频在线观看入口一二三2021 | 国内精品在线播放 | 黑人巨鞭大战白妞10级 | 国产裸舞福利资源在线视频 | 91精品国产美女福到在线不卡 | 色婷丁香 | 欧美伊香蕉久久综合类网站 | 国产成人夜色影视视频 | 深夜日韩| 久久亚洲成a人片 | 高清在线观看免费 | 亚洲精品久久久久AV无码 | 国产一二在线观看视频网站 | 国产精品第3页 | 日本一区二区高清免费不卡 | 高h辣文小说网 烧书阁 | 暖暖的视频完整视频韩国免费 | 欧美人人干 | 国产专区日韩精品欧美色 | 成人在线观看视频免费 | 美女脱了内裤让男生玩屁股 | 日本激情网 | 精品卡1卡2卡三卡免费网站 | 日韩一区二区三区四区区区 | 青青草原国产视频 | 好爽好深好猛好舒服视频上 | 色综合久久综精品 | 亚洲 另类 欧美 变态屎尿 | www.99热.com| 四虎影视4hu最新地址在线884 | 91po国产在线高清福利 | 欧美vpswindows动物 | chinses台湾男同志hd | 国产成人精品s8sp视频 | 久久这里只精品热在线18 | 国产福利免费看 | 青草视频在线观看免费资源 | 美女叽叽|