HashMap集合的自定義實現
HashMap集合是Map介面的實現類,在Map集合不同於Collectiion集合,Map集合存放的是鍵值對,通過鍵(key)可以找到對應的值(value),而且每一個key是唯一的。那麼該如何自定義實現HashMap呢?
通過閱讀jdk的原始碼,發現HashMap的底層資料結構其實就是陣列加上鍊表。筆者通過閱讀原始碼,自定義實現了HashMap。
數組裡面存放的是連結串列LinkedList,而連結串列裡存放的是Entry(表示一對鍵值對)。首先通過key的hash值計算一個出一個數值,把它當做陣列的索引(index),如果有兩個key計算得到的index相同,則這兩對鍵值對存放在索引為index的LinkedList當中。如果兩個key計算得到的index不同,這這兩個鍵值對會存放在不同的LinkedList當中。如圖:
具體的程式碼實現如下:
package com.tiantang.collection; /** * 自定義的Map集合介面 * @author LiuJinkun * */ public interface MyMap { /** * 返回集合的大小 * @return */ int size(); /** * 存放鍵值對 * @param key * @param value */ void put(Object key,Object value); /** * 根據鍵查詢值 * @param key * @return */ Object get(Object key); /** * 判斷集合是否為空 * @return */ boolean isEmpty(); /** * 移除鍵所對應的鍵值對 * @param key */ void remove(Object key); /** * 集合中是否包含指定的key * @param key * @return */ boolean containsKey(Object key); /** * 集合中是否包含指定的value值 * @param value * @return */ boolean containsValue(Object value); }
package com.tiantang.collection; import java.util.LinkedList; /** * 自定義的HashMap * * @author LiuJinkun * */ public class MyHashMap implements MyMap { private int size; // 用來存放LinkedList的陣列(陣列的下標用key所對應的hash值進行相應的計算而得到) private LinkedList<Entry>[] arr; /** * 無參構造器,預設初始化陣列容量為16 事實上,這裡陣列的初始化大小隻要求大於0即可,但過大會佔用太多的記憶體,過小則會影響查詢的效能 * 因此這裡根據參照原始碼,使其初始化大小為16(jdk原始碼根據負載均衡量會重新該表陣列的大小,這裡筆者簡單的實現,沒有考慮那麼詳細) */ public MyHashMap() { // 預設初始化陣列容量為16 this(16); } /** * 有參構造器 * * @param initialCapacity */ public MyHashMap(int initialCapacity) { if (initialCapacity <= 0) { throw new IllegalArgumentException(); } else { this.arr = new LinkedList[initialCapacity]; } } @Override public int size() { return size; } @Override public void put(Object key, Object value) { Entry entry = new Entry(key, value); int index = getIndex(key); if (arr[index] == null) { LinkedList<Entry> list = new LinkedList<Entry>(); list.add(entry); arr[index] = list; size++; } else { // 然後判斷key是否重複 for (int i = 0; i < arr[index].size(); i++) { //如果與集合中的key重複的就替換掉原來的value值 if (arr[index].get(i).getKey().equals(key)) { arr[index].get(i).value=value; return; } } //如果不重複,就新增 arr[index].add(entry); size++; } } @Override public Object get(Object key) { int index = getIndex(key); //獲得該索引處存放的連結串列 LinkedList<Entry> list=arr[index]; if(list!=null){ //遍歷連結串列,若果key相等就返回對應的value for(int i=0;i<list.size();i++){ if(list.get(i).key.equals(key)){ return list.get(i).value; } } } return null; } @Override public boolean isEmpty() { return size==0; } @Override public void remove(Object key) { int index = getIndex(key); LinkedList<Entry> list=arr[index]; if(list!=null){ for(int i=0;i<list.size();i++){ if(list.get(i).key.equals(key)){ list.remove(i); size--; return; } } } } @Override public boolean containsKey(Object key) { //根據key得到索引 int index = getIndex(key); LinkedList<Entry> list=arr[index]; if(list!=null){ for(int i=0;i<list.size();i++){ if(list.get(i).key.equals(key)){ return true; } } } return false; } /** * 根據key的hash值然後通過計算得到陣列索引 * 演算法為:hash值除以arr陣列的長度(這樣保證了得到的陣列索引是有效的) * @param key * @return */ private int getIndex(Object key) { int index=key.hashCode()%arr.length; return index; } @Override public boolean containsValue(Object value) { for(int i=0;i<arr.length;i++){ if(arr[i]!=null){ for(int j=0;j<arr[i].size();j++){ if(arr[i].get(j).value.equals(value)){ return true; } } } } return false; } private class Entry { Object key; Object value; public Object getKey() { return key; } public Object getValue() { return value; } public Entry(Object key, Object value) { this.key = key; this.value = value; } } }
上述程式碼只是簡單實現了Map裡面的部分方法,而且程式碼還可以在很大程度上優化,讀者如果有興趣可以自行研究。
下面是筆者又優化了部分程式碼後的結果:
package com.tiantang.collection;
import java.util.LinkedList;
/**
* 自定義的HashMap
*
* @author LiuJinkun
*
*/
public class MyHashMap implements MyMap {
private int size;
// 用來存放LinkedList的陣列(陣列的下標用key所對應的hash值進行相應的計算而得到)
private LinkedList<Entry>[] arr;
/**
* 無參構造器,預設初始化陣列容量為16 事實上,這裡陣列的初始化大小隻要求大於0即可,但過大會佔用太多的記憶體,過小則會影響查詢的效能
* 因此這裡根據參照原始碼,使其初始化大小為16(jdk原始碼根據負載均衡量會重新該表陣列的大小,這裡筆者簡單的實現,沒有考慮那麼詳細)
*/
public MyHashMap() {
// 預設初始化陣列容量為16
this(16);
}
/**
* 有參構造器
*
* @param initialCapacity
*/
public MyHashMap(int initialCapacity) {
if (initialCapacity <= 0) {
throw new IllegalArgumentException();
} else {
this.arr = new LinkedList[initialCapacity];
}
}
@Override
public int size() {
return size;
}
@Override
public void put(Object key, Object value) {
Entry entry = new Entry(key, value);
int index = getIndex(key);
if (arr[index] == null) {
LinkedList<Entry> list = new LinkedList<Entry>();
list.add(entry);
arr[index] = list;
size++;
} else {
// 然後判斷key是否重複
for (int i = 0; i < arr[index].size(); i++) {
//如果與集合中的key重複的就替換掉原來的value值
if (arr[index].get(i).getKey().equals(key)) {
arr[index].get(i).value=value;
return;
}
}
//如果不重複,就新增
arr[index].add(entry);
size++;
}
}
@Override
public Object get(Object key) {
int index = getIndex(key);
//獲得該索引處存放的連結串列
LinkedList<Entry> list=arr[index];
int i=getIndexOfNode(key, list);
if(i!=-1){
return list.get(i).value;
}
return null;
}
@Override
public boolean isEmpty() {
return size==0;
}
@Override
public void remove(Object key) {
int index = getIndex(key);
LinkedList<Entry> list=arr[index];
int i=getIndexOfNode(key, list);
if(i!=-1){
list.remove(i);
}
}
/**
* 根據key的hash值然後通過計算得到陣列索引
* 演算法為:hash值除以arr陣列的長度(這樣保證了得到的陣列索引是有效的)
* @param key
* @return
*/
private int getIndexOfNode(Object key,LinkedList<Entry> list){
if(list!=null){
for(int i=0;i<list.size();i++){
if(list.get(i).key.equals(key)){
return i;
}
}
}
return -1;
}
private int getIndex(Object key){
return key.hashCode()%arr.length;
}
@Override
public boolean containsKey(Object key) {
int index=getIndex(key);
return getIndexOfNode(key,arr[index])!=-1;
}
@Override
public boolean containsValue(Object value) {
for(int i=0;i<arr.length;i++){
if(arr[i]!=null){
for(int j=0;j<arr[i].size();j++){
if(arr[i].get(j).value.equals(value)){
return true;
}
}
}
}
return false;
}
private class Entry {
Object key;
Object value;
public Object getKey() {
return key;
}
public Object getValue() {
return value;
}
public Entry(Object key, Object value) {
this.key = key;
this.value = value;
}
}
}
相關推薦
HashMap的自定義實現
map() obj static void 定義 [] ram OS ava 一、背景: HashMap到底是怎麽實現的? 一對一對的存放,通過key找value;map的鍵不能重復;自己怎麽實現呢? 代碼: Wife.java 輔助類 pa
HashMap集合的自定義實現
HashMap集合是Map介面的實現類,在Map集合不同於Collectiion集合,Map集合存放的是鍵值對,通過鍵(key)可以找到對應的值(value),而且每一個key是唯一的。那麼該如何自定義實現HashMap呢? 通過閱讀jdk的原始碼,發現HashMap的底層
自定義實現類似WeakHashMap集合類
import java.lang.ref.WeakReference; /* * below methods have public idenifer that can be invoked by outer */ public class UserDefinedM
自定義實現Java中的ArrayList集合
最近準備找工作,就複習了下Java的基礎,順便多看看原始碼,在複習到集合這一章時,就想著自己動手實現集合,就看了看jdk的原始碼,由於筆者本科學的是高分子材料,和計算機、軟體、網際網路完全不沾邊,也沒學過資料結構,但為了多瞭解這方便的知識,就根據jdk的原始碼,模仿了集合的
自定義實現字符串string的接口
初始 定義類 per code enter public 自定義 truct this 用char*管理String類的內存,new動態分配,在析構函數中delete char*指向的new出來的內存,一個string類需要實現那些接口可參考標準庫裏的string: ht
vector的自定義實現
cnblogs logs name 成員變量 tor first ont const 技術 1 #pragma warning(disable:4996) 2 #include<iostream> 3 #include<string>
自定義實現復選框
列表 setw 屬於 隱藏 對象 p12 時間 下拉框 tar 項目中需要用到復選框,而QComboBox只能實現單選操作。即使是加以改造可以多選,也只能一次選擇一個選項,不符合項目需求。於是就花了兩天時間來自己實現一個可行的復選框。 實現方案:QLineEdit + QL
自定義實現Map類
text PE value lse [] rgs ext per ati 1 package text; 2 3 public class SxtMap001{ 4 SxtEntry[] arr = new SxtEntry[990]; 5 i
Flask中的session ,自定義實現 session機制, 和 flask-session組件
time 基礎 如何 password pyc class 原理 less pan session 是基於cookie實現, 保存在服務端的鍵值對(形式為 {隨機字符串:‘xxxxxx’}), 同時在瀏覽器中的cookie中也對應一相同的隨機字符串,
Xamarin自定義佈局系列——ListView的一個自定義實現ItemsControl(橫向列表)
原文: Xamarin自定義佈局系列——ListView的一個自定義實現ItemsControl(橫向列表) 在以前寫UWP程式的時候,瞭解到在ListView或者ListBox這類的列表空間中,有一個叫做ItemsPannel的屬性,它是所有列表中子元素實際的容器,如果要讓列表進行橫向排列,只需要在Xam
Ribbon負載均衡自定義實現
Ribbon簡介 負載均衡框架,支援可插拔式的負載均衡規則 支援多種協議,如HTTP、UDP等 提供負載均衡客戶端 Ribbon子模組 ribbon-core ribbon-eureka ribbon-httpclient 負載均衡器元件 一個負載均衡器,至少提供以下功能: 要維
react native自定義實現下拉重新整理上拉載入
1·定義元件 PageListView.js /** * 上拉重新整理/下拉載入更多 元件 */ import React, { Component } from 'react'; import { Text, View, ListView, FlatList
自定義實現向量圖示動畫VectorDrawable
前言 從5.0(API等級21)開始,android開始支援向量圖了。利用向量動畫可以實現一些很酷炫的效果。 前陣子有個需求要實現一個酷炫輸入框,利用向量動畫完美解決。 思路:畫個路徑,然後是加個分開和合並動畫 向量動畫結合TextInputLayout封裝成一個輸入
自定義實現資料庫連線池
資料庫連線池: >資料庫的連線物件建立工作,比較消耗效能 >一開始先在記憶體中開闢一塊空間(集合) , 先往池子裡面放置 多個連線物件。 後面需要連線的話,直接從池子裡面去。不要去自己建立連線了。 使用完畢, 要記得歸還連線。確保連線物件能迴圈利用。即建立
hadoop自定義實現排序流量統計
https://blog.csdn.net/wzcwmhp/article/details/53285581 首先map會按照key的預設字典排序規則對其輸出進行排序,如果我們想實現流量輸出排序,可以將其flowbean設定為key,然後通過compareable介面自定義排序規則對fl
Django中自定義實現restful
什麼是restful api 可以總結為一句話:REST是所有Web應用都應該遵守的架構設計指導原則。 Representational State Transfer,翻譯是”表現層狀態轉化”。 面向資源是REST最明顯的特徵,對於同一個資源的一組不同的操作。REST要求,必須通過統一的介面來對資源
Ext 6.5.3 classic版本,自定義實現togglefield開關控制元件
1,在Ext 6.5.3的classic版中沒有提供開關控制元件,參照modern版中 togglefield開關的實現,繼承滑動器(sliderfield),自定義一個開關按鈕。支援value繫結和點選切換狀態以及表單提交。 2,完成後效果如圖: 3, js程式碼如下:
Ext 6.5.3 classic版本,自定義實現togglefield開關控件
amp image 相關 led setvalue idt ext nic con 1,在Ext 6.5.3的classic版中沒有提供開關控件,參照modern版中 togglefield開關的實現,繼承滑動器(sliderfield),自定義一個開關按鈕。支持value
自定義實現SpringMvc框架,自定義@Controller、@RequestMapping註解,自己也是一步一步的對程式碼的理解出來的,只是比較簡單的例子
1.自定義的DispatcherServlet,就是SpringMvc執行載入類 /*** * 手寫SpringMvc框架<br> * 思路:<br> * 1.手動建立一個DispatcherServlet 攔截專案的所有請求 SpringMv
自定義實現橫向圓角進度條——簡易版
UI 說需要實現這樣圓角橫向進度條,好,於是我就去屁顛屁顛的 Google。下面就是我的辛酸歷程。 1、 設定 ProgressBar 的 android:progressDrawable 屬性 首先找到的一種實現方法就是為 ProgressBar 設定 a