最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中詳細解析Map接口

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

Map詳解:

先看圖,便于宏觀了解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是由數(shù)組和鏈表這兩個結構來存儲數(shù)據(jù)。

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

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

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

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

Entry: 存儲鍵值對。

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

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

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

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

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

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

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

Put方法:

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

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

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

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

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

Remove方法:

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

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

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

性質1:節(jié)點是紅色或黑色。

性質2:根節(jié)點是黑色。

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

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

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

詳細了解請點擊。

LinkedHashMap:

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

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

實現(xiàn)LRU緩存:

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

對比下Hashmap、Hashtable和ConcurrentHashmap:

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

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

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

總結

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

相關文章

  • Stream中的Peek操作代碼

    Stream中的Peek操作代碼

    這篇文章主要介紹了Stream中的Peek操作,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09
  • Java使用easyExcel批量導入數(shù)據(jù)詳解

    Java使用easyExcel批量導入數(shù)據(jù)詳解

    這篇文章主要介紹了Java使用easyExcel批量導入數(shù)據(jù)詳解,通常我們會提供一個模板,此模塊我們可以使用easyExcel導出數(shù)據(jù)生成的一個Excel文件當作模板,提供下載鏈接,用戶在該文件內填入規(guī)定的數(shù)據(jù)格式以后可以批量導入數(shù)據(jù)到數(shù)據(jù)庫中,需要的朋友可以參考下
    2023-08-08
  • log4j的配置文件詳細解析

    log4j的配置文件詳細解析

    以下小編主要為大家介紹一下log4j的配置文件各個配置項的含義。需要的朋友可以過來參考下
    2013-08-08
  • java生成自增編號數(shù)字的問題

    java生成自增編號數(shù)字的問題

    這篇文章主要介紹了java生成自增編號數(shù)字的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • springboot中spring.profiles.include的妙用分享

    springboot中spring.profiles.include的妙用分享

    這篇文章主要介紹了springboot中spring.profiles.include的妙用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • MyBatis-Plus實現(xiàn)連表查詢的方法實例

    MyBatis-Plus實現(xiàn)連表查詢的方法實例

    這篇文章主要給大家介紹了關于MyBatis-Plus實現(xiàn)連表查詢的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2022-01-01
  • 詳解MyBatis模糊查詢LIKE的三種方式

    詳解MyBatis模糊查詢LIKE的三種方式

    模糊查詢也是數(shù)據(jù)庫SQL中使用頻率很高的SQL語句,這篇文章主要介紹了詳解MyBatis模糊查詢LIKE的三種方式,非常具有實用價值,需要的朋友可以參考下
    2018-10-10
  • Java多線程實戰(zhàn)之單例模式與多線程的實例詳解

    Java多線程實戰(zhàn)之單例模式與多線程的實例詳解

    今天小編就為大家分享一篇關于Java多線程實戰(zhàn)之單例模式與多線程的實例詳解,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • 淺談Java之Map 按值排序 (Map sort by value)

    淺談Java之Map 按值排序 (Map sort by value)

    下面小編就為大家?guī)硪黄獪\談Java之Map 按值排序 (Map sort by value)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-08-08
  • Jenkins自動化部署springboot代碼實例

    Jenkins自動化部署springboot代碼實例

    這篇文章主要介紹了Jenkins自動化部署springboot代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04

最新評論

来安县| 九台市| 平果县| 五原县| 太仓市| 乳山市| 托里县| 荆门市| 徐水县| 盐亭县| 青浦区| 思茅市| 海门市| 安陆市| 城口县| 湄潭县| 中牟县| 大方县| 石首市| 淮阳县| 宣恩县| 鸡东县| 东山县| 罗山县| 和林格尔县| 宝山区| 大冶市| 紫阳县| 色达县| 自治县| 垫江县| 台湾省| 瑞金市| 宝应县| 白朗县| 大悟县| 瓮安县| 泗阳县| 麻城市| 宁乡县| 迭部县|