1. 程式人生 > >Java入門記(五):容器關係的梳理(下)——Map

Java入門記(五):容器關係的梳理(下)——Map

注意:閱讀本文及相關原始碼時,需要資料結構相關知識,包括:雜湊表、連結串列、紅黑樹。

  Map是將鍵(key)對映到值(value)的物件。不同的對映不能包含相同的鍵;每個鍵最多隻能對映到一個值。下圖是常見Map的介面和實現。與Collection相比,繼承關係簡單不少。

一、Map介面和AbstractMap抽象類

  Map介面除了增加對映、根據key獲取value、判斷對映中的key或value是否存在、刪除對映的基本方法外,還包含了返回包含所有key的Set、包含所有value的collection的方法。由於key不能重複,返回的Collection自然具有Set的屬性,很適合用Set返回。而value則不行。


  與其他Collection介面不同,Map介面中有一個子介面:Entry。Entry代表了一個對映,包含了key和value兩部分,同時,一個Enry的key沒有提供修改方法,而value允許修改。需要說明的是,如果用一個可變物件作為Map的key,若變化後equals()與之前的行為不同,那麼對映的行為是不確定的(JDK1.6文件)。

  對於抽象類AbstractMap,大部分實現的方法藉助了將所有entry組成的set返回的抽象方法entrySet():size()、isEmpty()(使用size())、containsValue()、containsKey()、get()、clear()、keySet()、values()等。而remove()、removeAll()、retainAll()、clear()、toString()則藉助了抽象方法iterator()。

  values()返回值value的是一個匿名內部類實現的AbstractCollection。value在第一次訪問時建立,在後續所有訪問中返回。雖然不進行元素的同步,其引用幾乎總是不變的,但返回值的行為會隨著Map中的元素變化:

public Collection<V> values() {
    if (values == null) {
        values = new AbstractCollection<V>() {
          public Iterator<V> iterator() {
             
return new Iterator<V>() {   private Iterator<Entry<K,V>> i = entrySet().iterator();   public boolean hasNext() {   return i.hasNext();   }   public V next() {    return i.next().getValue();   }   public void remove() {    i.remove();   }   }; }   public int size() {   return AbstractMap.this.size();   }   public boolean contains(Object v) {    return AbstractMap.this.containsValue(v);    } }; } return values; }

  對於Map.Entry,AbstractMap中實現了兩個鍵值對型別:SimpleEntry和SimpleImmutableEntry。後者與前者的區別是,不允許setValue(),呼叫該方法丟擲UnsupportedOperationException異常。

二、HashMap/LinkedHashMap/WeakHashMap

   在Java入門記(四):容器關係的梳理(上)——Collection一文中提到,HashSet/LinkedHashSet的底層實際是HashMap/LinkedHashMap。HashMap和一般的散列表實現方式相同,用陣列存放相同雜湊值的元素所組成佇列的首元素,佇列的元素是Entry,包括了key、value、hash值、next等屬性。尋找指定key時,先做雜湊,根據雜湊值找到陣列中對應的佇列頭,遍歷佇列找出key及對應的value。

  由於HashMap允許null作為key,這個key沒辦法做雜湊值的計算,只能遍歷雜湊值陣列,找到首元素的key為null的佇列。這個實現可以參考私有方法getForNullKey()。

  HashMap的key的雜湊值陣列有一個容量限制:必須為2的冪次。即使在新建HashMap時或呼叫resize()時指定一個非2的冪次的容量,實際呼叫時新的HashMap容量也會擴大到不小於這個指定容量的2的冪次的值。與之相關聯地,雜湊值的計算hash()和某個hash值在陣列的索引的計算indexFor()方式為:

static int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
    return h & (length-1);
}

2的冪次可以保證計算索引時適當的截斷(捨棄高位)。

   LinkedHashMap與HashMap的關係並不像LinkedList和ArrayList那樣。從結構上來看,LinkedHashMap僅僅是把所有的Entry組成了一個雙向連結串列。這樣,在迭代遍歷時,可以使用插入順序或LRU順序訪問所有元素(通過設定accessOrder標記位)。

  WeakHashMap和HashMap很類似,其內部包含了一個ReferenceQueue,並且它的Entry是繼承自WeakReference的。通過這種方式,在clear()、resize()、size()、getTable()時,都會呼叫expungeStaleEntries()方法,垃圾回收掉不再使用的對映關係。這裡不介紹Reference的相關內容了。

  思考下上一篇文章所提出的問題:是不是可以先實現HashSet,再用HashSet實現HashMap?個人認為,這樣實現的HashSet中的元素(對應Map的Entry),只有鍵沒有值,是無法直接實現HashMap的。

二、Hashtable/Properties

  Hashtable雖然實現了Map介面,但沒有用AbstractMap來做。它的行為與HashMap很相似,保留下來是為了相容原來的程式碼,不推薦繼續使用。

  繼承了Hashtable<Object,Object>的Properties稍有點不同,它與流的關係密切些,可儲存在流中或從流中載入。另外,它是執行緒安全的。

三、SortedMap和TreeMap

  SortedMap中的所有元素都是排過序的。這個“排序”不同於LinkedHashMap中將所有元素組織成一個連結串列,而是指任意任意兩個元素都可以比較大小關係,並根據這個比較規則Comparator進行排序。更準確的說,是鍵的大小關係。建立在有序的基礎上,SortedMap介面中包含了返回部分Map的方法subMap(K fromKey, K toKey)、headMap(K toKey)、tailMap(K fromKey)以及首尾key的方法firstKey()、lastKey()。

  TreeMap是SortedMap的一個實現,其Compartor可以為null,這種情況下比較元素大小時呼叫元素自身的compareTo()方法。

  TreeMap實際上使用了紅黑樹,儲存了樹的根。關於紅黑樹的演算法,講起來可以單獨開一篇文章,這裡不展開了,想了解的讀者可以讀下《演算法導論》的相關章節。TreeMap的元素插入、刪除其實是紅黑樹節點的插入和刪除。在元素有序的前提下,找到特定的key(以及對應的value)同樣是使用了紅黑樹的查詢方法。

相關推薦

Java入門()容器關係梳理——Map

注意:閱讀本文及相關原始碼時,需要資料結構相關知識,包括:雜湊表、連結串列、紅黑樹。   Map是將鍵(key)對映到值(value)的物件。不同的對映不能包含相同的鍵;每個鍵最多隻能對映到一個值。下圖是常見Map的介面和實現。與Collection相比,繼承關係簡單不少。 一、Map介面和Abs

Java入門(四)容器關係梳理——Collection

      目錄 三、Set   Java.util中的容器又被稱為Java Collections framework。雖然被稱為框架,但是其主要目的是提供一組介面儘量簡單而且相同、並且儘量高效、以便於開發人員按照場景選用,而不

Godot3遊戲引擎入門上下左右移動動畫

一、前言 本篇是上一節文章:Godot3遊戲引擎入門之五:上下左右移動動畫(上)的繼續。上一篇使用動畫和程式碼實現了玩家的上下左右移動功能,接下來我們解決一個問題:給遊戲新增碰撞體,讓玩家在有限的地圖中移動。 注意:我目前使用的是 Godot 3.1 預覽版,與

Java入門(二)向上轉型與向轉型

interface CanFight { void fight(); } interface CanSwim { void swim(); } interface CanFly { void fly(); } class ActionCharacter {

51微控制器入門數碼管顯示動態

本文旨在介紹微控制器入門的基礎知識,為初接觸或即將接觸單片的新手提供一個入門指導。本文章會陸續推出,隔幾天一個章節。所使用微控制器為ATMEL公司的AT89C52,軟體為PROTEUS和KEIL;只提供原理圖和KEIL環境下的註釋,希望對廣大即將接觸微控制器的人有所幫助,

Java入門(三)初始化順序

初始化順序的規則 1.在一個類的物件例項化時,成員變數首先初始化,然後才呼叫構造器,無論書寫順序。如果呼叫構造器前,沒有顯式初始化,那麼會賦預設值。 這樣做法的原因可以理解為:構造器執行時可能會用到一些成員變數的初值。 2.static變數早於所有其他的類成員變數初始化,同樣無論書寫順序。但是stati

Java入門(一)折騰HelloWorld

  HelloWorld,學習每門語言的第一步。有人戲稱,這些年的程式設計生涯就是學習各種語言的HelloWorld,不知是自謙還是自嘲。目前所在的公司使用Java作為主要開發語言,我進行語言轉換也大半年了,這HelloWorld便是語言轉換的第一關。好在本科的時候學過那麼一點,而且在此之前進行了較長時間的C

kaggle入門項目Titanic存亡預測數據處理

理解 ima 簡單 標識符 數據處理 let ger 好的 元素 原kaggle比賽地址:https://www.kaggle.com/c/titanic 原kernel地址:A Data Science Framework: To Achieve 99% Accuracy

java字符串

for 字符串分割 .com start abcd h+ equal 兩個 小寫 判斷字符串是否相等 對字符串對象進行比較不能簡單地比較運算符“====”,因為比較運算符比較的是兩個字符串的地址是否相同。即使兩個字符串的內容相同。 即使兩個字符串的內容相同,兩個對象的內存地

智能合約從入門到精通Lib工具庫

空間 creator arr 進行 符號 libjson sos 介紹 ray 簡介:上一節,我們介紹智能合約開發中常用的Lib工具庫的第一部分。由於內容較長,工具庫我們將分兩部分介紹,本文將介紹Lib工具庫的第二部分:LibJson 、LibStack和LibLog。Li

Java設計模式簡介行為型模式

其實每個設計模式都是很重要的一種思想,看上去很熟,其實是因為我們在學到的東西中都有涉及,儘管有時我們並不知道,其實在Java本身的設計之中處處都有體現,像AWT、JDBC、集合類、IO管道或者是Web框架,裡面設計模式無處不在。因為我們篇幅有限,很難講每一個設計模式都講的很詳細。 本章講講

路一步步走>> 設計模式Singleton-單例單件

package com.test.DPs.ChuangJian.Singleton; /** * 建立型:Singleton-單例(單件) * * 單例模式-Singleton * 用途:保證一個類僅有一個例項,並提供一個訪問他的全域性訪問點。 */ public class Si

敏捷開發績效管理之敏捷開發生產率故事點估算

這是敏捷開發績效管理的第五篇。(之一,之二,之三,之四,之五,之六,之七)度量敏捷開發的生產率一直是個難題,確切說度量任何開發方法的生產率都是一個難題,但它實際上有答案,這個答案是本文的主要內容。度量敏捷生產率的目的真正難以回答的是度量生產率的目的是什麼?很多人都認為是考核績

機器學習筆記廣義線性模型GLM

一.指數分佈族 在前面的筆記四里面,線性迴歸的模型中,我們有,而在logistic迴歸的模型裡面,有。事實上,這兩個分佈都是指數分佈族中的兩個特殊的模型。所以,接下來會仔細討論一下指數分佈族的一些特點,會證明上面兩個分佈為什麼是指數分佈族的特性情況以及怎麼用到

Java讀取Excel資料基於Apache POI

Java讀取Excel資料:基於Apache POI(一) Java本身不支援直接讀取微軟的Excel表格資料。第三方的Apache提供了一個庫POI用以支援Java讀寫Excel表格資料。 首先需要到Apache官網下載POI的庫,下載連結地址:https://poi.apache.org

高階資料庫十查詢優化器

Optimizer Implementation(Part I) 背景 在講述這個優化器的時候,就必須先了解查詢過程。在本系列的資料庫四:淺談資料庫查詢過程(Query Processing)中大致地說明了一下資料庫的查詢過程,但是沒提到查詢優化器的具體

五大常用演算法之分支限界法

轉載自:http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741378.html 一、基本描述     類似於回溯法,也是一種在問題的解空間樹T上搜索問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標

【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十FIFO儲存模組同步

實驗十五:FIFO儲存模組(同步) 筆者雖然在實驗十四曾解釋儲存模組,而且也演示奇怪的傢伙,但是實驗十四隻是一場遊戲而已。至於實驗十五,筆者會稍微嚴肅一點,手動建立有規格的儲存模組,即同步FIFO。那些看過《時序篇》的同學一定對同步FIFO不會覺得陌生吧?因為筆者曾在《時序篇》建立基於移位暫存器的同步FIF

OpenCV從入門到放棄摸魚筆記

    零cpp基礎的小白探索OpenCV從從入門到放棄的摸魚記錄從這裡開始。     參考書:《OpenCV3程式設計入門》毛星雲、冷雪飛 等編著 電子工業出版社 一、OpenCV(Open Source Computer Vision Library)開源計算機視覺庫

OPEN(SAP) UI5 學習入門系列之二 最佳實踐練習

可以先把程式碼下載到本地並跑起來,這樣可以對這個最佳實踐的程式有一個直觀的瞭解。 頁面導航如下:  銷售訂單列表(Master) -> 銷售訂單明細(Detail) -> 行專案明細(LineItem),在每個明細頁面都可以返回到上一層。 具體頁面之間的導航是如何實現的呢?  我們從頁面的入口