1. 程式人生 > >HashMap集合的自定義實現

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