結合JDK源碼看設計模式——組合模式

来源:https://www.cnblogs.com/Cubemen/archive/2019/04/08/10673712.html
-Advertisement-
Play Games

前言: 相信大家都打開過層級很多很多的文件夾。如果把第一個文件夾看作是樹的根節點的話,下麵的子文件夾就可以看作一個子節點。不過最終我們尋找的還是文件夾中的文件,文件可以看做是葉子節點。下麵我們介紹一種模式,與這種樹級結構息息相關。當然,今天的主角是HashMap。接下來我們一起來看HashMap中到 ...


前言:

  相信大家都打開過層級很多很多的文件夾。如果把第一個文件夾看作是樹的根節點的話,下麵的子文件夾就可以看作一個子節點。不過最終我們尋找的還是文件夾中的文件,文件可以看做是葉子節點。下麵我們介紹一種模式,與這種樹級結構息息相關。當然,今天的主角是HashMap。接下來我們一起來看HashMap中到底是怎麼跟樹級結構進行掛鉤的。

一、定義

  將對象組合成樹形結構以表示“部分-整體”的一個層次結構,使客戶端對單個對象和組合對象保持一致的方式處理。

二、適用場景

1、客戶端可以忽略組合對象與單個對象的差異

  註意組合模式中的概念,當客戶端使用組合模式的時候是對單個對象和組合對象保持一致的方式處理,而不是不同的方式處理。所以如果客戶端可以忽略組合對象和單個對象的差異時,才用組合模式。

2、處理一個樹形結構

  這裡就很好理解了,組合模式就是處理樹形結構的

三、結合HashMap看組合模式

  1、抽象構件:總的抽象類或者介面,定義一些通用的方法,比如新增、刪除。

  2、中間構件:繼承或者實現抽象構件,定義存儲方式,並針對特殊需要重寫抽象構件的方法。

  3、葉子節點:繼承或者實現抽象構件,並針對特殊需要重寫抽象構件的方法。

  葉子節點需要實現或者繼承抽象構件,如果有需要也可以添加到中間構件上。就比如說我現在進入D盤,D盤就是一個抽象構件,無論在D盤哪裡,我都可以執行新建,刪除操作。中間構件就相當於文件夾,裡面定義了相應的存儲方式,加入的文件是葉子節點,並且會按照這種方式來存儲,同時這個中間構件也可以選擇再加入一個其他中間構件或者自己。葉子節點就可以看作文件。註意,在組合模式下所有類都需要直接或者間接,繼承或實現總的抽象構件。下麵講HashMap中的putAll()方法

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
    }
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }
}

  上面是簡化的HashMap。我們看見putAll方法傳入的是Map對象,Map就是一個抽象構件(同時這個構件中只支持鍵值對的存儲格式),而HashMap是一個中間構件,HashMap中的Node節點就是葉子節點。說到中間構件就會有規定的存儲方式。HashMap中的存儲方式是一個靜態內部類的數組Node<K,V>[] tab。下麵是簡化的具體代碼

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
  
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                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;
}
}

  所以我們調用put方法實際上是加入一個葉子節點,而我們調用putAll相當於把其他Map下麵的文件夾中的文件拷貝過來。還有Hashtable也可以看作一個中間構件,裡面的putAll方法同樣是適合放入實現Map介面的對象。這裡如果將Hashtable的這個中間構件放入HashMap中,那麼我們這些方法還能用嗎。答案是能用,組合模式處理的就是一致性的問題。Map介面中的方法,比如put(),remove(),到下麵的中間構件中其實都是一致的功能,不過就是不同的中間構件中的處理方式可能會有細微的差別。下麵是我的測試代碼

public class Test {

    public static void main(String[] args) {
  Map<Integer,String> rootmap=new HashMap<Integer,String>();
  rootmap.put(1,"HashMap文件1");
  Map<Integer,String> map1=new Hashtable<Integer,String>();
  map1.put(2,"Hashtable文件1");
  map1.put(3,"Hashtable文件2");
  map1.put(4,"Hashtable文件3");
  rootmap.putAll(map1);
  System.out.println(rootmap);
 } }

輸出結果為:

  上面實現將Hashtable中的鍵值對放入HashMap中。如果放在組合模式下,你就可以看作是將Hashtable這個中間構件下的文件拷貝到HashMap這個中間構件中。在我們電腦里文件任何類型的文件都可以拷到其他任意地方。但是用來理解Map這個抽象構件的就不行,因為實現Map介面的類只支持放入鍵值對格式的葉子節點,如果不是(比如說ArrayList)就不行,這是底層方法不支持。同時我們回到組合模式上重要的一點:一致性,Map下麵添加都是put方法,需要傳入兩個值,List下都是add方法,只需要傳入一個值,如果List中的可以通過調用putAll方法放入Map里,那麼我該怎麼給List賦key或value呢。(不是說不能有put(1,list)這樣的操作,而是說在組合模式下操作時候需要有一致性,並且這裡說的組合模式針對的是putAll方法,與其他方法無關)

四、總結

  這裡只是解析了HashMap中的putAll組合模式。在平常寫代碼的過程中要使用組合模式,就需要先定義一個抽象類或者介面。在這裡就可以看作是抽象構件,主要實現一些基本的通用的功能。接著如果有需要就要定義一個中間構件,並且實現抽象構件,裡面的方法可以根據特殊需要重寫。而且這個類最重要的是要有存儲結構在裡面,用Map存儲,或者用List存儲等等都可以。接著就是我們真正的葉子節點,同樣是繼承或實現抽象構件,裡面的方法可以根據特殊需要重寫。當你看完這篇博客,你可以再去看定義和適用場景,說不定收穫會更大。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 具體效果如右下角 需要三個文件和一個可選文件 waifu.css(看板娘在頁面的位置以及大小) waifu-tips.js(看板娘的語言設置) live2d.min.js(一些點擊之後的動作) *flat-ui.min.css(看板娘的選項 PS:右面的選項 ) https://pan.baidu. ...
  • 網站項目經常會遇到一些視頻或者圖片素材的展示功能,那麼就需要自己寫一個功能,就是在一些列表頁面你想要是這個數據的詳細內容,彈框在頁面某個位置 例如這是視頻懸浮展示效果,可自定義自動播放等屬性標簽 又例如這是圖片懸浮展示,可控制懸浮展示與不展示 像上面這樣或者一些其他列表做的一些懸浮彈框展示等,都可以 ...
  • JavaScript_01簡介,基本語法,運算符,和java語言進行對比總結 ...
  • 1,element 表格嵌入CheckBox 效果圖如下: 2,element結合checkBox實現單選效果如下: html代碼: js代碼: ...
  • "use strict": 1、它是 ES5 引入的一條指令,指令不是語句,但非常接近於語句 2、不包含任何語言的關鍵字,指令僅僅是一個包含一個特殊字元串直接量的表達式。對於那些沒有實現 ES5 的 JavaScript 解釋器來說,它只是一條沒有副作用的表達式語句 3、只能出現在腳本代碼的開始或函 ...
  • HTML部分 js部分 ...
  • 接手項目之後,在安裝依賴後,再npm start的過程中報錯./node_modules/history/esm/history.js Module not found: Can't resolve '@babel/runtime/helpers/esm/extends' ...
  • 最近一個星期準備學習一下angular前端框架,因為之前在學習abp框架的時候,都要求前端要掌握angular,所以不得不回來惡補一下了,學習的過程有時間的話會記錄在這裡,方便以後複習。 閑言少敘,下麵來介紹開發環境搭建的步驟: 開發環境搭建 1.安裝node和npm(其實只需要安裝node,因為n ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...