HashMap真面目

背景

今天數據采集項目碰到一個性能問題,3000多個采集點,每一個采集點每秒送一個數據,接收到數據之后首先需要內存中做緩存,之后有一系列的業務分析處理,所以,對系統性能要求比較高。

最近幾天發現服務器cpu大多時間都在100%以上,所以就重點分析了緩存方案,確實是緩存方案有問題,修改之后初步驗證,cpu降低到50%左右,更換了第二個緩存方案之后,cpu降低到了大概30%左右。

所以,不同的緩存方案,對于高并發場景下應用性能的影響還是蠻大的。

java中的緩存,大部分都是通過HashMap實現的,突然想到之前就記錄過HashMap學習筆記,找了半天才找到,差點丟了,重新找回來做個記錄。

搞一個Map學習系列,從HashMap開始。

認識HashMap

HashMap是java中應用最廣的集合類之一,以key/value(鍵值對)的方式保存數據。

你可以把HashMap叫做集合類,也可以把它叫做容器,java中許多容器框架比如Spring,其實好多都是用HashMap來存儲數據的。

當然,java秉承“一切都是對象”,HashMap中存儲的當然也是對象,只不過是以“鍵值”對組成的對象。

HashMap繼承自AbstractMap,并實現了Map接口。所以,想要徹底搞懂HashMap,還是需要先從Map接口、以及AbstractMap入手。

Map接口

其實Map接口沒啥東西,接口而已。定義了size()、isEmpty()、get()、put()、containsKey()、containsValue()…等通用的Map類方法。

相對重要一點的是,Map接口定義了一個Entry<K,V>內部接口,這個Entry其實就是Map包含的對象,不同的Map的實現類會有不同的實現。

Map接口也實現了幾個方法,具體暫時就不詳細分析了,這其實是一個很好的針對“接口是否可以實現方法?”這個問題的很好的答案。

AbstractMap抽象類

AbstractMap抽象類實現了Map接口,具體化了部分Map接口定義的方法。

實現了一個叫SimpleEntry的Entry(就是Map接口中定義的內部接口),還有一個叫SimpleImmutableEntry的Entry。

暫且不表,不影響主題:識別HashMap真面目。

HashMap的數據結構

回過頭來再繼續研究HashMap,首先識別HashMap的數據結構,我們先從簡單的入手,一步一步抽絲剝繭、先易后難,逐步研究。

首先來認識一下Node。
Node是Entry的實現,數據結構非常簡單:

 final int hash;final K key;V value;Node<K,V> next;

哈希值、key、vaue以及指向下一節點的簡單的鏈表結構。

HashMap桶內Node鏈表容量增大之后會自動修改簡單鏈表結構為紅黑樹,本篇暫不研究紅黑樹。

table數組
table數組是HashMap真正存儲數據的地方,所以說白了HashMap底層實際上還是數組。

不過HashMap的table是比較特殊的數組,數組內的每一個對象其實就是我們常說的,桶內裝的是Node<K,V>對象,也就是我們放置到HashMap中的鍵值對。

HashMap的初始化

HashMap提供了幾個不同的初始化方法,區別無非就是有沒有初始化容量大小、有沒有初始化對象、有沒有初始化的loadFactor和threshold。

這幾個概念需要我們一個個慢慢去了解。

  1. 容量
    就是HashMap中table數組的大小,HashMap的容量是2的n次方,初始化不設置容量的話,默認16,初始化如果設置了
    initialCapacity 的話,則HashMap的容量是最接近initialCapacity并且大于initialCapacity的2的整數次冪,比如initialCapacity設置為3,4,5則HaspMap最終容量為8,設置為9,10,11…則HashMap的最終容量為16,以此類推。

    這個容量計算是通過tableSizeFor方法實現的,我們暫時按下好奇心(這個方法為什么能實現大于輸入參數cap的最近的2的整數次冪?)。

static final int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;}
  1. loadFactor
    直譯為裝載因子,意譯為擴容因子,初始值設置為0.75。
  2. threshold
    threshold是擴容閾值,HashMap的容量loadFactor=threshold。當HashMap存放對象的數量增長到threshold的時候進行擴容。
    比如HashMap初始化容量為16,默認loadFactor為0.75,則threshold=16
    0.75=12。則當HashMap存儲對象的數量(size)大于12的時候,HaspMap調用resize()方法自動擴容。
    4.size
    可以通過調用size()方法獲取到,HashMap內實際存放的對象數。我們如果要想搞清楚HashMap的真面目,最好能對size和容量Capacity有清楚的認識。size是對應用很有價值的數據,我們開發過程中所說的一個HashMap的大小其實指的就是size。而Capacity對應用來說沒有什么意義,只是HashMap內部使用的概念,只對那些對HashMap內部結構有興趣、想要研究清楚其工作機制的同學有意義。

###HashMap賦值
HashMap的賦值邏輯如下(假設待存放的數據為e<key1,value1>):

  1. 檢查table數組為空的話,初始化指定容量或者默認容量的table數組
  2. 根據key1的哈希值計算得出(算法為(容量 - 1) & hash(key1))對應的桶。這一步很重要,一般來講優秀的hash算法能夠盡可能確保不同的key值得到不同的hash值,也就可以確保放入不同的桶內。但是不可避免的,可能會存在不同key值得到相同hash值的情況(hash沖突:key1<>key2,hash(key1)=hash(key2)),這種情況下就會放置在相同的桶(比如table[5])內。
  3. 得到桶之后,判斷桶內是否已經有數據。
  4. 沒有數據則直接new一個Node:newNode(hash, key1, value1, null),放在桶中,結束。
  5. 否則,桶內有數據,有兩種情況:一是為鍵值key1重復賦值、二是hash沖突。
  6. 如果是hash沖突,則new一個Node:newNode(hash, key1, value1, null)并將其設置為桶內的最后一個Node。
  7. 如果是重復賦值(桶內數據的key值=key1),則為key1重新賦值value1,并返回key1的舊值

所以我們可以看到,針對key而言,HashMap不重復,意思是說,相同的key只在HashMap中只保留一份數據。

并且,一般情況下,HashMap的一個桶內只保留一個對象,只有在hash沖突發生了之后,桶內才有可能放置多于一個對象,以鏈表結構保存。

HashMap中的null對象

此處null對象指的是HashMap中的key值為null的Node對象。

大家都知道HashMap允許且僅能存儲key=null的一個對象,比如代碼:

    HashMap hm<String,String> = new HashMap();hm.put(null,"it's null");hm.put(null,"i am null");hm.put(null,"null loves null");

最終hm容器中只有一個null對象,并且hm.get(null)得到的應該是 “null loaves null”。

這背后的原因可以從上述HashMap賦值邏輯中找到答案,你只要知道null的hash值是0就可輕易得出以上結論。

從HashMap獲取數據

通過get(key)方法獲取數據的邏輯如下(假設要獲取的數據key=key1):

  1. table數組不為空并且數組長度大于0,則采用與put數據相同的算法得到key1值對應的桶。
  2. 桶內不空則從第一個節點開始檢查,如果節點key值等于key1,則返回該節點的value。如果第一個節點不滿足條件,則依次檢查桶內所有其他節點。
  3. 桶內空,或者桶內不空但是沒有找到滿足條件的對象(應該不可能)則返回null,表明當前HashMap中不存在key值為key1的對象

需要注意的一點是,檢查節點key值等于key1的邏輯是:
兩個對象相等,或者兩個對象不為null且key1.equals(key)。

好了,以上!相信已經能夠揭開HashMap神秘面紗之一角了。

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

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

相關文章

STM32CubeMX-H7-19-ESP8266通信(中)--單片機控制ESP8266實現TCP地址通信

前言 上篇文章我們已經能夠使用串口助手實現esp8266的幾種通信&#xff0c;接下來我們使用單片機控制實現。這篇文章會附帶教程&#xff0c;增加.c和,.h&#xff0c;把串口和定時器放到對應的編號&#xff0c;然后調用初始化就可以使用了。 先講解&#xff0c;然后末尾再放源碼…

歐盟RED網絡安全標準EN 18031-2的要求

歐盟RED網絡安全標準EN 18031-2的要求 歐盟RED網絡安全標準EN 18031-2的要求 ? 適用產品范圍&#xff1a; 能夠處理個人隱私數據的可聯網無線電設備。 不具備聯網能力的三類無線電設備&#xff1a;玩具、兒童護理類設備、可穿戴類設備。 主要測試與評估內容&#xff1a; EN…

一起了解--CAST函數

CAST函數在SQL中用途廣泛&#xff0c;不僅可以轉換為數值類型&#xff0c;還可以在多種場景下用于數據類型轉換。以下是一些常見的用途和示例&#xff1a; 類型轉換 使用CAST函數可以在查詢數據庫時根據需要調整數據格式或類型 CAST(expression AS target_type) expression …

(50)課71:查看指定 query_id 的 SQL 語句的執行各個階段的耗時情況 show profile for query query_id;

&#xff08;137&#xff09;查看指定 query_id 的 SQL 語句的執行各個階段的耗時情況 show profile for query query_id &#xff1a; &#xff08;138&#xff09; 謝謝

AWS中國云的定時任務(AWS EventBridge+AWS Lambda)

問題 最近有一個每天在凌程定時同步數據給第三方系統的需求。需要使用AWS EventBridge和AWS Lambda結合的方式來同步數據給第三方系統。 思路 使用Python的ORM框架(例如&#xff1a;SQLAlchemy)查詢到需要同步的數據&#xff0c;然后&#xff0c;使用http客戶端&#xff08;…

開源PSS解析器

本章介紹開源PSS解析工具&#xff1a; 1. PSSTools語法解析器&#xff0c;這個工具僅包含一個語法解析器。 2. gen-pss&#xff0c;實現了語法解析器&#xff0c;和簡單的Test realization&#xff0c;沒有約束求解器。 本文將改造并使用gen-pss來生成C測試用例&#xff0…

《linux2.4 內存管理》:第 2 章 描述物理內存

Linux 適用于多種體系結構&#xff0c;需用體系結構無關方式描述內存。本章介紹影響 VM 行為的內存簇、頁面和標志位結構。 非一致內存訪問&#xff08;NUMA&#xff09;&#xff1a;在 VM 中&#xff0c;大型機器內存分簇&#xff0c;依簇與處理器距離&#xff0c;訪問代價不…

數據湖是什么?數據湖和數據倉庫的區別是什么?

目錄 一、數據湖是什么 &#xff08;一&#xff09;數據湖的定義 &#xff08;二&#xff09;數據湖的特點 二、數據倉庫是什么 &#xff08;一&#xff09;數據倉庫的定義 &#xff08;二&#xff09;數據倉庫的特點 三、數據湖和數據倉庫的區別 &#xff08;一&#…

Smart Form Adobe form

強制更改內表:TNAPR se16-> Smart Form總覽 Smart form 變量格式說明: &symbol& (括號中,小寫字母為變量) &symbol& 屏蔽從第一位開始的N位 &symbol (n)& 只顯示前N位 &symbol (S)& 忽略正負號 &symbol (<)& 符號在…

Linux 內核學習(11) --- Linux 鏈表結構

文章目錄 Linked List 簡介Linked List 操作方法鏈表頭結點初始化創建鏈表節點添加節點到鏈表中從鏈表中刪除節點從鏈表中替換節點移動鏈表中的節點檢查鏈表鏈表遍歷demo 實例 Linked List 簡介 鏈表是一種數據結構&#xff0c;由一系列節點組成&#xff0c;每個節點包含數據部…

一分鐘部署nginx-公網IP訪問內網

前言 服務器內網下有nacos cluster&#xff08;3個節點&#xff09;&#xff0c;開放到公網并指定公司網絡訪問需要配置三次IP白名單&#xff0c;因此需要簡化流程&#xff0c;通過nginx反向代理只配置1次IP白名單。 現在通過docker容器模擬環境&#xff0c;準備1臺云服務器。…

C 語言分支與循環

目錄 一. 分支結構&#xff1a;if 語句與 switch 語句 1. if 語句 2. switch 語句 二、關系操作符、條件操作符與邏輯操作符 1. 關系操作符 2. 條件操作符 3. 邏輯操作符 三、循環結構&#xff1a;while 循環、for 循環與 do - while 循環 1. while 循環 2. for 循…

【一文看懂Spring Boot2.x升級Spring Boot3.x】springboot2.x升級springboot3.x

springboot2.x升級springboot3.x 背景升級jdk版本為17以上springboot版本修改javax包更新mybatis-plus升級swagger升級springdocspringdoc配置背景 當前項目是springboot2.5.9版本的springboot+mybatis-plus項目,需要升級到springboot3.5.0項目。 升級jdk版本為17以上 Spri…

陽臺光伏防逆流電表革新者:安科瑞ADL200N-CT/D16-WF

——為家庭能源管理提供高精度、智能化解決方案 一、陽臺光伏爆發的背景 在全球能源轉型與碳中和目標的驅動下&#xff0c;陽臺光伏正以革命性姿態重塑家庭能源消費模式。從歐洲的“微型發電站”到中國的“萬億藍海”&#xff0c;這一創新技術不僅撬動了能源市場的結構性變革…

美團完整面經

面試崗位 面試的崗位 - 2025春季校招 【轉正實習】軟件服務工程師-后端方向&#xff08;成都 - 軟硬件服務-SaaS事業部&#xff09; 一面&#xff08;業務初試 - 30min&#xff09; 問題 自我介紹 Java基礎 HashMap底層用的數據結構是什么&#xff1f;是線程安全的嗎&…

pysnmp 操作流程和模塊交互關系的可視化總結

1. SNMP GET 操作序列圖 #mermaid-svg-KALvv8WkHJTsNCeu {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-KALvv8WkHJTsNCeu .error-icon{fill:#552222;}#mermaid-svg-KALvv8WkHJTsNCeu .error-text{fill:#552222;str…

關于 /proc/net/tcp 與 /proc/$pid/net/tcp 的關系分析

關于 /proc/net/tcp 與 /proc/$pid/net/tcp 的關系分析 1. 基礎概念 在 Linux 系統中&#xff0c;每個進程必定歸屬于一個且僅一個網絡命名空間&#xff08;Network Namespace&#xff09;。這是 Linux 命名空間隔離機制的核心特性之一。 /proc/net/tcp 顯示當前網絡命名空間…

微信小程序 - 保存手機號等信息到通訊錄

主要使用小程序 wx.addPhoneContact 這個api 一、界面 <view class"tab-item" bindtap"addToPhoneContacts">保存</view> 二、js 邏輯文件中 addToPhoneContacts() {wx.addPhoneContact({firstName: this.data.firstName, // 姓名mobilePh…

計算機視覺一些定義解析

1.GCT&#xff08;Gated Channel Transformation&#xff09; 定義 GCT&#xff08;Gated Channel Transformation&#xff09;是一種用于增強卷積神經網絡特征提取能力的模塊。它的核心思想是通過門控機制對特征圖的通道進行動態調整&#xff0c;從而突出對任務更有幫助的特…

美團NoCode的Database 使用指南

系列文章目錄 第一篇&#xff1a;美團NoCode設計網站的嘗試經驗分 第二篇&#xff1a;美團NoCode中的Dev Mode 使用指南 文章目錄 系列文章目錄Database 適用場景一、什么是 Database&#xff1f;二、準備流程1. 申請賬號 三、使用流程1.申請資源的同時可搭建 NoCode 頁面&…