那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Map類集合基本元素的實現(xiàn)演變

科技綠洲 ? 來源:Java技術(shù)指北 ? 作者:Java技術(shù)指北 ? 2023-10-10 16:14 ? 次閱讀

說到集合類,之前介紹的ArrayList類,HashMap可能是大家日常用的最多的類,但是對于另一個集合類 LinkedHashMap,可能大家用的不多,但是這種鏈?zhǔn)焦<希行┣闆r確實特別好用。

1、LinkedHashMap 定義

LinkedHashMap 是基于 HashMap 實現(xiàn)的一種集合,具有 HashMap 集合上面所說的所有特點,除了 HashMap 無序的特點,LinkedHashMap 是有序的,因為 LinkedHashMap 在 HashMap 的基礎(chǔ)上單獨維護了一個具有所有數(shù)據(jù)的雙向鏈表,該鏈表保證了元素迭代的順序。

所以我們可以直接這樣說:LinkedHashMap = HashMap + LinkedList。LinkedHashMap 就是在 HashMap 的基礎(chǔ)上多維護了一個雙向鏈表,用來保證元素迭代順序。

更形象化的圖形展示可以直接移到文章末尾。

public class LinkedHashMap< K,V >
    extends HashMap< K,V >
    implements Map< K,V >

圖片

2、字段屬性

①、Entry

static class Entry< K,V > extends HashMap.Node< K,V > {
        Entry< K,V > before, after;
        Entry(int hash, K key, V value, Node< K,V > next) {
            super(hash, key, value, next);
        }
    }

LinkedHashMap 的每個元素都是一個 Entry,我們看到對于 Entry 繼承自 HashMap 的 Node 結(jié)構(gòu),相對于 Node 結(jié)構(gòu),LinkedHashMap 多了 before 和 after 結(jié)構(gòu)。

下面是Map類集合基本元素的實現(xiàn)演變。

圖片

LinkedHashMap 中 Entry 相對于 HashMap 多出的 before 和 after 便是用來維護 LinkedHashMap 插入 Entry 的先后順序的。

②、其它屬性

//用來指向雙向鏈表的頭節(jié)點
transient LinkedHashMap.Entry< K,V > head;
//用來指向雙向鏈表的尾節(jié)點
transient LinkedHashMap.Entry< K,V > tail;
//用來指定LinkedHashMap的迭代順序
//true 表示按照訪問順序,會把訪問過的元素放在鏈表后面,放置順序是訪問的順序
//false 表示按照插入順序遍歷
final boolean accessOrder;

注意:這里有五個屬性別搞混淆的,對于 Node next 屬性,是用來維護整個集合中 Entry 的順序。對于 Entry before,Entry after ,以及 Entry head,Entry tail,這四個屬性都是用來維護保證集合順序的鏈表,其中前兩個before和after表示某個節(jié)點的上一個節(jié)點和下一個節(jié)點,這是一個雙向鏈表。后兩個屬性 head 和 tail 分別表示這個鏈表的頭節(jié)點和尾節(jié)點。

3、構(gòu)造函數(shù)

①、無參構(gòu)造

public LinkedHashMap() {
        super();
        accessOrder = false;
    }

調(diào)用無參的 HashMap 構(gòu)造函數(shù),具有默認初始容量(16)和加載因子(0.75)。并且設(shè)定了 accessOrder = false,表示默認按照插入順序進行遍歷。

②、指定初始容量

public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

③、指定初始容量和加載因子

public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

4、添加元素

LinkedHashMap 中是沒有 put 方法的,直接調(diào)用父類 HashMap 的 put 方法。關(guān)于 HashMap 的put 方法,可以參看我之前對于 HashMap 的介紹。

我將方法介紹復(fù)制到下面:

//hash(key)就是上面講的hash方法,對其進行了第一步和第二步處理
   public V put(K key, V value) {
       return putVal(hash(key), key, value, false, true);
   }
   /**
    *
    * @param hash 索引的位置
    * @param key  鍵
    * @param value  值
    * @param onlyIfAbsent true 表示不要更改現(xiàn)有值
    * @param evict false表示table處于創(chuàng)建模式
    * @return
    */
   final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
           boolean evict) {
        Node< K,V >[] tab; Node< K,V > p; int n, i;
        //如果table為null或者長度為0,則進行初始化
        //resize()方法本來是用于擴容,由于初始化沒有實際分配空間,這里用該方法進行空間分配,后面會詳細講解該方法
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //注意:這里用到了前面講解獲得key的hash碼的第三步,取模運算,下面的if-else分別是 tab[i] 為null和不為null
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);//tab[i] 為null,直接將新的key-value插入到計算的索引i位置
        else {//tab[i] 不為null,表示該位置已經(jīng)有值了
            Node< K,V > e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;//節(jié)點key已經(jīng)有值了,直接用新值覆蓋
            //該鏈?zhǔn)羌t黑樹
            else if (p instanceof TreeNode)
                e = ((TreeNode< K,V >)p).putTreeVal(this, tab, hash, key, value);
            //該鏈?zhǔn)擎湵?/span>
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //鏈表長度大于8,轉(zhuǎn)換成紅黑樹
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    //key已經(jīng)存在直接覆蓋value
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;//用作修改和新增快速失敗
        if (++size > threshold)//超過最大容量,進行擴容
            resize();
        afterNodeInsertion(evict);
        return null;
   }

5、刪除元素

同理也是調(diào)用 HashMap 的remove 方法,這里我不作過多的講解,著重看LinkedHashMap 重寫的第 46 行方法。

public V remove(Object key) {
        Node< K,V > e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }

    final Node< K,V > removeNode(int hash, Object key, Object value,
            boolean matchValue, boolean movable) {
        Node< K,V >[] tab; Node< K,V > p; int n, index;
        //(n - 1) & hash找到桶的位置
        if ((tab = table) != null && (n = tab.length) > 0 &&
        (p = tab[index = (n - 1) & hash]) != null) {
        Node< K,V > node = null, e; K k; V v;
        //如果鍵的值與鏈表第一個節(jié)點相等,則將 node 指向該節(jié)點
        if (p.hash == hash &&
        ((k = p.key) == key || (key != null && key.equals(k))))
        node = p;
        //如果桶節(jié)點存在下一個節(jié)點
        else if ((e = p.next) != null) {
            //節(jié)點為紅黑樹
        if (p instanceof TreeNode)
         node = ((TreeNode< K,V >)p).getTreeNode(hash, key);//找到需要刪除的紅黑樹節(jié)點
        else {
         do {//遍歷鏈表,找到待刪除的節(jié)點
             if (e.hash == hash &&
                 ((k = e.key) == key ||
                  (key != null && key.equals(k)))) {
                 node = e;
                 break;
             }
             p = e;
         } while ((e = e.next) != null);
        }
        }
        //刪除節(jié)點,并進行調(diào)節(jié)紅黑樹平衡
        if (node != null && (!matchValue || (v = node.value) == value ||
                      (value != null && value.equals(v)))) {
        if (node instanceof TreeNode)
         ((TreeNode< K,V >)node).removeTreeNode(this, tab, movable);
        else if (node == p)
         tab[index] = node.next;
        else
         p.next = node.next;
        ++modCount;
        --size;
        afterNodeRemoval(node);
        return node;
        }
        }
        return null;
    }

6、查找元素

public V get(Object key) {
        Node< K,V > e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
            afterNodeAccess(e);
        return e.value;
    }

相比于 HashMap 的 get 方法,這里多出了第 5,6行代碼,當(dāng) accessOrder = true 時,即表示按照最近訪問的迭代順序,會將訪問過的元素放在鏈表后面。

對于 afterNodeAccess(e) 方法,在前面第 4 小節(jié) 添加元素已經(jīng)介紹過了,這就不在介紹。

7、遍歷元素

在介紹 HashMap 時,我們介紹了 4 中遍歷方式,同理,對于 LinkedHashMap 也有 4 種,這里我們介紹效率較高的兩種遍歷方式:

①、得到 Entry 集合,然后遍歷 Entry

LinkedHashMap< String,String > map = new LinkedHashMap<  >();
        map.put("A","1");
        map.put("B","2");
        map.put("C","3");
        map.get("B");
        Set< Map.Entry< String,String >> entrySet = map.entrySet();
        for(Map.Entry< String,String > entry : entrySet ){
            System.out.println(entry.getKey()+"---"+entry.getValue());
        }

②、迭代

Iterator< Map.Entry< String,String >> iterator = map.entrySet().iterator();
        while(iterator.hasNext()){
            Map.Entry< String,String > entry = iterator.next();
            System.out.println(entry.getKey()+"----"+entry.getValue());
        }

8、小結(jié)

好了,這就是JDK中java.util.LinkedHashMap 類的介紹。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    7145

    瀏覽量

    89583
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4346

    瀏覽量

    62977
  • MAP
    MAP
    +關(guān)注

    關(guān)注

    0

    文章

    49

    瀏覽量

    15180
  • 元素
    +關(guān)注

    關(guān)注

    0

    文章

    47

    瀏覽量

    8469
收藏 人收藏

    評論

    相關(guān)推薦

    【labview我來告訴你】實現(xiàn)任何LabVIEW數(shù)據(jù)類型集合的簡潔方式

    則是元素排列按一定順序的集合。Variant attributes 提供了一個非常好而簡潔的方式來實現(xiàn)任何 LabVIEW 數(shù)據(jù)類型的集合。 比如我們想創(chuàng)建一個字符串的有序
    發(fā)表于 12-30 09:30

    【labview我來告訴你】簡潔的實現(xiàn)任何 LabVIEW 數(shù)據(jù)類型的集合

    而簡潔的方式來實現(xiàn)任何 LabVIEW 數(shù)據(jù)類型的集合。 比如我們想創(chuàng)建一個字符串的有序集合,我們可以通過使用Set Variant Attribute 來追加新的元素迚去。作為 Va
    發(fā)表于 01-11 09:52

    簡潔的實現(xiàn)任何 LabVIEW 數(shù)據(jù)類型的集合

    而簡潔的方式來實現(xiàn)任何 LabVIEW 數(shù)據(jù)類型的集合。 比如我們想創(chuàng)建一個字符串的有序集合,我們可以通過使用Set Variant Attribute 來追加新的元素迚去。作為 Va
    發(fā)表于 12-17 09:51

    java集合干貨系列

    :List列表、Set集合Map映射、工具(Iterator迭代器、Enumeration枚舉、Arrays和Collections)。  Collection接口、子接口以及
    發(fā)表于 12-14 15:11

    干貨!對 buck-boost 進行演變,最終會演變成 flyback

    演變成更復(fù)雜的拓撲結(jié)構(gòu),那么我們?nèi)跁炌ǖ睦斫飧鞣N拓撲結(jié)構(gòu),就變得非常容易。其實理解隔離電源,相對非隔離DCDC來說,需要多理解一個基本元素——變壓器。然后很多基本原理也可以通過基本拓撲進行演變。本文
    發(fā)表于 01-12 16:09

    python入門知識:什么是set集合

    集合set set集合是一個無序不重復(fù)元素的集,基本功能包括關(guān)系測試和消除重復(fù)元素集合使用大括號({})框定
    發(fā)表于 09-24 16:29

    python集合

    python集合集合(英文名 set),它是一個無序的不重復(fù)元素序列。這里面有兩個重點:無序,不重復(fù)1. 創(chuàng)建集合集合的創(chuàng)建有兩種方法第一種方法:使用 花括號 {} 直接創(chuàng)建,創(chuàng)建的時候,{} 可以
    發(fā)表于 02-23 17:04

    HarmonyOS方舟開發(fā)框架容器API的介紹與使用

    作者:liuxin,華為高級工程師 容器,顧名思義就是存儲的,用于存儲各種數(shù)據(jù)類型的元素,并具備一系列處理數(shù)據(jù)元素的方法。在方舟開發(fā)框架中,容器
    發(fā)表于 03-07 11:40

    JAVA集合匯總

    多數(shù)情況下使用。 二、層次關(guān)系 如圖所示:圖中,實線邊框的是實現(xiàn),折線邊框的是抽象,而點線邊框的是接口 Collection 接口是集合
    的頭像 發(fā)表于 01-16 11:50 ?3831次閱讀
    JAVA<b class='flag-5'>集合</b><b class='flag-5'>類</b>匯總

    數(shù)組與集合說明

    對數(shù)組與集合進行詳細介紹。
    發(fā)表于 03-17 14:28 ?6次下載
    數(shù)組與<b class='flag-5'>集合</b><b class='flag-5'>類</b>說明

    什么是 map

    map 容器,又稱鍵值對容器,即該容器的底層是以紅黑樹變體實現(xiàn)的,是典型的關(guān)聯(lián)式容器。這意味著,map 容器中的元素可以分散存儲在內(nèi)存空間里,而不是必須存儲在一整塊連續(xù)的內(nèi)存空間中。跟
    的頭像 發(fā)表于 02-27 15:41 ?3123次閱讀

    python集合表達式及方法

    python數(shù)字的集合(set)類型,是無序集合集合中的元素(項)不會重復(fù),不管添加多少個相同元素(項),只會保存1次。
    的頭像 發(fā)表于 03-10 10:06 ?1387次閱讀

    Java8的Stream流 map() 方法

    之后,對集合可以進行 Stream 操作,使上面的處理更簡潔。 概述 Stream 流式處理中有 map() 方法,先看下其定義,該方法在java.util.stream.Stream中 可以看到
    的頭像 發(fā)表于 09-25 11:06 ?2032次閱讀
    Java8的Stream流 <b class='flag-5'>map</b>() 方法

    List 轉(zhuǎn) Map的方法

    在我們平時的工作中,充滿了各種類型之間的轉(zhuǎn)換。今天小編帶大家上手 List 轉(zhuǎn) Map 的各種操作。 我們將假設(shè) List 中的每個元素都有一個標(biāo)識符,該標(biāo)識符將在生成的 Map 中作為一個鍵
    的頭像 發(fā)表于 10-09 16:10 ?1700次閱讀

    LinkedHashSet 和 LinkedHashMap定義

    Map 集合實現(xiàn)了 Set 集合。 1、LinkedHashSet 定義 LinkedHa
    的頭像 發(fā)表于 10-10 15:10 ?544次閱讀
    LinkedHashSet 和 LinkedHashMap定義
    真人百家乐| 百家乐规| 大发888游戏平台hanpa| 修水县| 百家乐官网787| 百家乐官网便利| 希尔顿百家乐试玩| 网页棋牌游戏| 百家乐官网平注法技巧| 百家乐视频地主| 大发888怎么玩能赢| 澳门百家乐官网网址| 高楼24层风水好吗| 全讯网六仔开奖| 苏尼特左旗| 十六浦百家乐官网的玩法技巧和规则| 网上百家乐的玩法技巧和规则 | 百家乐官网庄闲局部失衡| 中华百家乐官网的玩法技巧和规则| 太阳百家乐开户| 太阳会百家乐官网现金网| 百家乐真人游戏网| 捕鱼棋牌游戏| 百家乐官网百胜注码法| 真人百家乐送钱| 宾利百家乐官网现金网| 百家乐投注双赢技巧| 娱乐城注册| 百家乐官网社区| 大发888 3403| 利澳百家乐官网娱乐城| 网上百家乐公| 任我赢百家乐官网软件中国有限公司 | 顶级赌场371betcwm| 百家乐官网最安全打法| 定制百家乐桌垫| 讷河市| 百家乐试玩1000元| 百家乐官网双面数字筹码| 十三张百家乐的玩法技巧和规则 | 正规博彩通|