java集合AbstractMap_Java 集合中的 AbstractMap 抽象類

Java 集合中的 AbstractMap 抽象類

jdk1.8.0_144

AbstractMap 抽象類實現了一些簡單且通用的方法, 本身并不難但在這個抽象類中有兩個方法非常值得關注, keySet 和 values 方法源碼的實現可以說是教科書式的典范

抽象類通常作為一種骨架實現, 為各自子類實現公共的方法上一篇我們講解了 Map 接口, 此篇對 AbstractMap 抽象類進行剖析研究

Java 中 Map 類型的數據結構有相當多, AbstractMap 作為它們的骨架實現實現了 Map 接口部分方法, 也就是說為它的子類各種 Map 提供了公共的方法, 沒有實現的方法各種 Map 可能有所不同

抽象類不能通過 new 關鍵字直接創建抽象類的實例, 但它可以有構造方法 AbstractMap 提供了一個 protected 修飾的無參構造方法, 意味著只有它的子類才能訪問 (當然它本身就是一個抽象類, 其他類也不能直接對其實例化), 也就是說只有它的子類才能調用這個無參的構造方法

在 Map 接口中其內部定義了一個 Entry 接口, 這個接口是 Map 映射的內部實現用于維護一個 key-value 鍵值對, key-value 存儲在這個 Map.Entry 中 AbstractMap 對這個內部接口進行了實現, 一共有兩個: 一個是可變的 SimpleEntry 和一個是不可變的 SimpleImmutableEntry

public static class SimpleEntry implements Entry, java.io.Serializable

實現了 Map.Entry 接口, 并且實現了 Serializable(可被序列化)

它的方法比較簡單都是取值存值的操作, 對于 key 值的定義是一個 final 修飾意味著是一個不可變的引用另外其 setValue 方法稍微特殊, 存入 value 值返回的并不是存入的值, 而是返回的以前的舊值需要重點學習的是它重寫的 equals 和 hashCode 方法publicbooleanequals(Objecto){

if(!(oinstanceofMap.Entry))// 判斷參數是否是 Map.Entry 類型, 要 equals 相等首先得是同一個類型

returnfalse;

Map.Entry,?>e=(Map.Entry,?>)o;// 將 Object 類型強轉為 Map.Entry 類型, 這里參數使用? 而不是 K, V 是因為泛型在運行時類型會被擦除, 編譯器不知道具體的 K,V 是什么類型

returneq(key,e.getKey())&&eq(value,e.getValue());//key 和 value 分別調用 eq 方法進行判斷, 都返回 ture 時 equals 才相等

}

privatestaticbooleaneq(Objecto1,Objecto2){

returno1==null?o2==null:o1.equals(o2);// 這個三目運算符也很簡單, 只不過需要注意的是盡管這里 o1o2 是 Object 類型, Object 類型的 equals 方法是通過 == 比較的引用, 所以不要認為這里有問題, 因為在實際中, o1 類型有可能是 String, 盡管被轉為了 Object, 所以此時在調用 equals 方法時還是調用的 String#equals 方法

}

要想正確重寫 equals 方法并能正確使用, 通常還需要重寫 hashCode 方法publicinthashCode(){

return(key==null?0:key.hashCode())^(value==null?0:value.hashCode());//key 和 value 的值不為 null 時, 將它們的 hashCode 進行異或運算

}

publicstaticclassSimpleImmutableEntryimplementsEntry,java.io.SerializableSimpleImmutableEntry

定義為不可變的 Entry, 其實是事實不可變, 因為它不提供 setValue 方法, 在多個線程同時訪問時自然不能通過 setValue 方法進行修改它相比于 SimpleEntry 其 key 和 value 成員變量都被定義為了 final 類型調用 setValue 方法將會拋出 UnsupportedOperationException 異常

它的 equals 和 hashCode 方法和 SimpleEntry 一致

接下來查看 AbstractMap 抽象類實現了哪些 Map 接口中的方法

public int size()

Map 中定義了一個 entrySet 方法, 返回的是 Map.Entry 的 Set 集合, 直接調用 Set 集合的 size 方法即是 Map 的大小

public boolean isEmpty()

調用上面的 size 方法, 等于 0 即為空

public boolean containsKey(Object key)

這個方法的實現較為簡單, 通過調用 entrySet 方法獲取 Set 集合的迭代器遍歷 Map.Entry, 與參數 key 比較 Map 可以存儲為 null 的 key 值, 由于 key=null 在 Map 中存儲比較特殊 (不能計算 hashCode 值), 所以在這里也做了判斷參數 key 是否為空

public boolean containsValue(Object value)

這個方法實現和 containsKey 一致

public V get(Object key)

這個方法實現和上面兩個也類似, 不同的是上面相等返回 boolean, 這個方法返回 value 值

public V put(K key, V value)

向 Map 中存入 key-value 鍵值對的方法并沒有具體實現, 會直接拋出一個 UnsupportedOperationException 異常

public V remove(Object key)

通過參數 key 刪除 Map 中指定的 key-value 鍵值對這個方法也很簡單, 也是通過迭代器遍歷 Map.Entry 的 Set 集合, 找到對應 key 值, 通過調用 Iterator#remove 方法刪除 Map.Entry

public void putAll(Map extends K, ? extends V> m)

這個方法也很簡單遍歷傳入的 Map, 調用 put 方法存入就可以了

public void clear()

調用 entrySet 方法獲取 Set 集合再調用 Set#clear() 方法清空

public Set keySet()

返回 Map key 值的 Set 集合 AbstractMap 中定義了一個成員變量 transient Set keySet, 在 JDK7 中 keySet 變量是由 volatile 修飾的, 但在 JDK8 中并沒有使用 volatile 修飾在對 keySet 變量的注釋中解釋道, 訪問這些字段的方法本身就沒有同步, 加上 volatile 也不能保證線程安全關于 keySet 方法的實現就有點意思了

首先思考該方法是返回 key 值的 Set 集合, 很自然的能想到一個簡單的實現方式, 遍歷 Entry 數組取出 key 值放到 Set 集合中, 類似下面代碼:publicSetkeySet(){

Setks=null;

for(Map.Entryentry:entrySet()){

ks.add(entry.getKey());

}

returnks;

}

這就意味著每次調用 keySet 方法都會遍歷 Entry 數組, 數據量大時效率會大大降低不得不說 JDK 源碼是寫得非常好, 它并沒有采取遍歷的方式如果不遍歷 Entry, 那又如何知道此時 Map 新增了一個 key-value 鍵值對呢?

答案就是在 keySet 方法內部重新實現了一個新的自定義 Set 集合, 在這個自定義 Set 集合中又重寫了 iterator 方法, 這里是關鍵, iterator 方法返回 Iterator 接口, 而在這里又重新實現了 Iterator 迭代器, 通過調用 entrySet 方法再調用它的 iterator 方法下面結合代碼來分析:publicSetkeySet(){

Setks=keySet;// 定義的 transient Set keySet

if(ks==null){// 第一次調用肯定為 null, 則通過下面代碼創建一個 Set 示例

ks=newAbstractSet(){// 創建一個自定義 Set

publicIteratoriterator(){// 重寫 Set 集合的 iterator 方法

returnnewIterator(){// 重新實現 Iterator 接口

privateIterator

V>>i=entrySet().iterator();// 引用 Entry 的 Set 集合 Iterator 迭代器

publicbooleanhasNext(){

returni.hasNext();// 對 key 值的判斷, 就是對 entry 的判斷

}

publicKnext(){

returni.next().getKey();// 取下一個 key 值, 就是取 entry#getKey

}

publicvoidremove(){

i.remove();// 刪除 key 值, 就是刪除 entry

}

};

}

publicintsize(){// 重寫的 Set#size 方法

returnAbstractMap.this.size();//key 值有多少就是整個 Map 有多大, 所以調用本類的 size 方法即可這個是內部類, 直接使用 this 關鍵字代表這個類, 應該指明是調用 AbstractMap 中的 size 方法, 沒有 this 則表示是 static 靜態方法

}

publicbooleanisEmpty(){// 重寫的 Set#isEmpty 方法

returnAbstractMap.this.isEmpty();// 對是否有 key 值, 就是判斷 Map 是否為空,, 所以調用本類的 isEmpty 方法即可

}

publicvoidclear(){// 重寫的 Set#clear 方法

AbstractMap.this.clear();// 清空 key 值, 就是清空 Map,, 所以調用本類的 clear 方法即可

}

publicbooleancontains(Objectk){// 重寫 Set#contains 方法

returnAbstractMap.this.containsKey(k);// 判斷 Set 是否包含數據 k, 就是判斷 Map 中是否包含 key 值, 所以調用本類的 containsKey 方法即可

}

};

keySet=ks;// 將這個自定義 Set 集合賦值給變量 keySet, 在以后再次調用 keySet 方法時, 因為 keySet 不為 null, 只需直接返回

}

returnks;

我認為這是一種很巧妙的實現, 盡管這個方法是圍繞 key 值, 但實際上可以結合 Entry 來實現, 而不用遍歷 Entry, 同時上面提到了調用 entrySet# iterator 方法, 這里則又是模板方法模式的最佳實踐因為 entrySet 在 AbstractMap 中并未實現, 而是交給了它的子類去完成, 但是對于 keySet 方法卻可以對它進行一個算法骨架 實現, 這就是模板方法模式

public Collection values()

對于 values 方法則完全可以參考 keySet, 兩者有著異曲同工之妙, 這里為節省篇幅不再贅述

public abstract Set> entrySet()

一個抽象方法, 交給它的子類去完成, 說明這個方法并不是特別通用

public boolean equals(Object o)

Map 中規定只有在 Map 中的每對 key-value 鍵值對的 key 和 value 都一一對應時他們的 equals 比較才返回 true 在方法中先判斷簡單的條件, 如果引用相等, 直接返回 true, 如果參數 o 不是 Map 類型直接返回 false, 如果兩個 Map 的數量不同也直接返回 false 后面才再遍歷 Entry 數組比較 Entry 中的 key 和 value 是否一一對應方法簡單, 但這給了我們一個啟示, 在條件判斷中, 先判斷簡單的基本的, 再判斷復雜的

public int hashCode()

重寫了 Object 類的 equals 方法, 重寫 hashCode 也是必須的 AbstractMap 對 hashCode 的實現是將所有 Map.Entry(這里就是 SimpleEntry 或 SimpleImmutableEntry) 的 hashCode 值向加, 最后得出的總和作為 Map 的 hashCode 值

public String toString()

這個方法沒什么好說的, 就是取出所有鍵值對使用 StringBuilder 對其進行拼接

protected Object clone() throws CloneNotSupportedException

實現一個淺拷貝, 由于是淺拷貝對于變量 keySet 和 values 不進行拷貝, 防止兩個淺拷貝引發的問題, 關于 Object 中的 clone 方法在萬類之父 Object 已有解析

來源: https://www.cnblogs.com/yulinfeng/p/8486539.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/394072.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/394072.shtml
英文地址,請注明出處:http://en.pswp.cn/news/394072.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leetcode392. 判斷子序列(動態規劃)

給定字符串 s 和 t &#xff0c;判斷 s 是否為 t 的子序列。 你可以認為 s 和 t 中僅包含英文小寫字母。字符串 t 可能會很長&#xff08;長度 ~ 500,000&#xff09;&#xff0c;而 s 是個短字符串&#xff08;長度 <100&#xff09;。 字符串的一個子序列是原始字符串刪…

讓機器讀懂用戶——大數據中的用戶畫像

讓機器讀懂用戶——大數據中的用戶畫像 摘要&#xff1a; 用戶畫像(persona)的概念最早由交互設計之父Alan Cooper提出:“Personas are a concrete representation of target users.” 是指真實用戶的虛擬代表&#xff0c;是建立在一系列屬性數據之上的目標用戶模型。隨著互聯…

asp.net應用程序_如何在ASP.NET中為聊天應用程序構建鍵入指示器

asp.net應用程序by Neo Ighodaro由新Ighodaro 如何在ASP.NET中為聊天應用程序構建鍵入指示器 (How to build a typing indicator for your chat app in ASP.NET) A basic understanding of ASP.NET and jQuery is needed to follow this tutorial.要學習本教程&#xff0c;需要…

activeMQ在文件上傳的應用

本次試驗主要用到了activeMq和上傳插件uploadify的知識&#xff0c;感謝以下兩篇文章的作者。 1.http://itindex.net/detail/47160-java-jquery-%E4%B8%8A%E4%BC%A0 2.http://blog.csdn.net/jiuqiyuliang/article/details/47160259 本文中不再提供activeMq和uploadify的介紹。 …

java nginx 例子_Java及nginx實現文件權限控制代碼實例

我們知道&#xff0c;使用nginx作為文件下載服務器&#xff0c;可以極大地降低對后端Java服務器的負載沖擊&#xff0c;但是nginx本身并不提供授權控制&#xff0c;因此好的方案是由后端服務器實現權限控制&#xff0c;最好的方式是直接復用應用的認證體系&#xff0c;最大化的…

leetcode934. 最短的橋(dfs+bfs)

在給定的二維二進制數組 A 中&#xff0c;存在兩座島。&#xff08;島是由四面相連的 1 形成的一個最大組。&#xff09; 現在&#xff0c;我們可以將 0 變為 1&#xff0c;以使兩座島連接起來&#xff0c;變成一座島。 返回必須翻轉的 0 的最小數目。&#xff08;可以保證答…

謝煙客---------Linux之DNS服務系統的基礎知識

DNS Domain Name Server1)C/S架構&#xff1a;SOCKET通信IP PORT2&#xff09;應用層協議&#xff1a;資源子網BIND Berkerley Information Name DomainDNS由來1&#xff09;統一名字&#xff0c;自己維護 <自己查詢>解析: 基于key查找value: 查詢數據庫(二維關系的表: …

Java實現點擊導出excel頁面遮罩屏蔽,下載完成后解除遮罩

一、問題場景 最近在做數據統計功能&#xff0c;需求是導出大數據量的excel&#xff0c;時間間隔較長&#xff0c;大概需要十秒左右&#xff0c;點擊導出后&#xff0c;頁面沒有做任何處理&#xff0c;用戶也不知道是否正在導出&#xff1b;如果沒有做交互上的限制&#xff0c;…

pbs 支持 java_Linux下Java安裝與配置

安裝以JDK1.6.0_43為例下載jdk-6u43-linux-x64.bin&#xff0c;http://www.oracle.com/technetwork/java/javase/downloads/index.html增加可執行權限 chmod x jdk-6u43-linux-x64.bin&#xff0c;執行 ./jdk-6u43-linux-x64.bin 生成目錄jdk1.6.0_43拷貝到/usr/share下&#x…

使用Chatkit構建Node.js命令行聊天應用程序

by Hugo雨果 使用Chatkit構建Node.js命令行聊天應用程序 (Build a Node.js command-line chat application with Chatkit) Building chat in your app can be pretty complex. Yet, with Chatkit, implementing fully-featured chat is nothing but a few lines of code.在您的…

java 毫秒轉分鐘和秒_Java程序將毫秒轉換為分鐘和秒

Java程序將毫秒轉換為分鐘和秒在上面的程序中&#xff0c;您將學習如何在Java中將毫秒分別轉換為分鐘和秒。示例1&#xff1a;將毫秒分別轉換為分鐘和秒import java.util.concurrent.TimeUnit;public class Milliseconds {public static void main(String[] args) {long millis…

Andrew Ng機器學習之一 導論

監督學習與無監督學習 監督學習&#xff08;Supervised Learning) Ng的原文是&#xff1a; We gave the algorithm a data set that the "right answers" were given. 即給定了一個正確結果的集合供算法學習&#xff0c;強調了需要實現準備好正負樣本喂給機器。 無監…

leetcode994. 腐爛的橘子(bfs)

在給定的網格中&#xff0c;每個單元格可以有以下三個值之一&#xff1a; 值 0 代表空單元格&#xff1b; 值 1 代表新鮮橘子&#xff1b; 值 2 代表腐爛的橘子。 每分鐘&#xff0c;任何與腐爛的橘子&#xff08;在 4 個正方向上&#xff09;相鄰的新鮮橘子都會腐爛。 返回直…

ES6對象的擴展

1.屬性簡寫表示 2.方法簡寫表示 屬性與方法簡寫&#xff1a; 3.屬性名表達式 ES6允許字面量定義對象時&#xff0c;用方法二&#xff08;表達式&#xff09;作為對象的屬性名&#xff0c;即把表達式放在方括號內。 4.Object.is()比較兩個值是否嚴格相等 轉載于:https://www.cnb…

Spring Cloud項目MVN編譯 -- Non-resolvable import POM

最近利用閑余時間&#xff0c;打算搭建一套基于Spring Cloud G版的微服務架構(Spring boot 2.1.0)&#xff0c;一頓操作之后,IDEA也沒有提示什么錯誤,自認為微服務搭建完畢。啟動項目前&#xff0c;習慣性的Maven -clean了一下&#xff0c;我去&#xff0c;IDEA里面的Maven Pro…

datax底層原理_Datax 插件加載原理

Datax 插件加載原理插件類型Datax有好幾種類型的插件&#xff0c;每個插件都有不同的作用。reader&#xff0c; 讀插件。Reader就是屬于這種類型的writer&#xff0c; 寫插件。Writer就是屬于這種類型的transformer&#xff0c; 目前還未知handler&#xff0c; 主要用于任務執行…

mysql windows身份驗證_SQL Server 2005 怎么就不能用Windows身份驗證方式登錄呢?

SQL Server 2005 自從裝到我的電腦上始終無法使用Windows身份驗證的方式登錄,由于使用用戶名和密碼登錄還算順暢,所以一直忽略了這SQL Server 2005 自從裝到我的電腦上始終無法使用Windows身份驗證的方式登錄,由于使用用戶名和密碼登錄還算順暢,所以一直忽略了這個問題,直到又有…

JavaScript正則表達式快速簡單的指南

Interested in learning JavaScript? Get my ebook at jshandbook.com有興趣學習JavaScript嗎&#xff1f; 在jshandbook.com上獲取我的電子書 正則表達式簡介 (Introduction to Regular Expressions) A regular expression (also called regex for short) is a fast way to w…

leetcode104. 二叉樹的最大深度(dfs)

給定一個二叉樹&#xff0c;找出其最大深度。二叉樹的深度為根節點到最遠葉子節點的最長路徑上的節點數。說明: 葉子節點是指沒有子節點的節點。示例&#xff1a; 給定二叉樹 [3,9,20,null,null,15,7]&#xff0c;3/ \9 20/ \15 7 返回它的最大深度 3 。代碼 class Soluti…

[解讀REST] 3.基于網絡應用的架構

鏈接上文[解讀REST] 2.REST用來干什么的&#xff1f;&#xff0c;上文中解釋到什么是架構風格和應該以怎樣的視角來理解REST&#xff08;Web的架構風格&#xff09;。本篇來介紹一組自洽的術語&#xff0c;用它來描述和解釋軟件架構&#xff1b;以及列舉下對于基于網絡的應用來…