手写HashMap实现

一、理解说明是哈希表,hashmap的实现原理

哈希表由数组+链表进行实现,见下图可进行说明,理解哈希表的内存模型和特点对于理解hashmap非常重要。哈希表的使用场景非常广泛,比如memcache其实就是一张非常大的哈希表。

 

二、重新hashmap的方法,主要理解思想

 

package demo.netty_lihongmin.map;

/**
 * 抽取自己的HashMap接口
 * @author kevin
 *
 */
public interface MyMap<K, V> {

	V put(K k, V v);
	
	V get(K k);
	
	// hashMap内部的Entry对象
	interface Entry<K, V>{
		
		public K getKey();
		
		public V getValue();
	}
}

 

 

 

 

package demo.netty_lihongmin.map;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MyHashMap<K, V> implements MyMap<K, V> {

	// 定义默认数组的大小  2^n
	private static int defaultLength = 1<<4;
	
	/**
	 * 1、哈希表内部的数组的长度,若使用的数组长度超过总长度的0.75 则数组需要进行扩容,扩容将就是将数组长度变为原来的两倍,
	 * 	    并且将现在的数组内的数据重新hash并均匀的散列分布到新的数组中,所以该操作消耗比较大
	 * 2、defaultDaaSizeFactory值是经过测试得出,考虑各种因素后的均衡值,值过大会照成扩容的概率下降,消耗少但是存取数据的效率也降低(反之。。。)
	 * 3、0.9或者更大的扩容值,会内置形成大量的链表,存取都需要进行很多的next()判定查找操作
	 */
	private static double defaultAddSizeFactory = 0.75;
	
	/**
	 * 已使用数组的长度(put操作或判定并进行++操作)
	 */
	private int userSize;
	
	// 数组之一(即数组中的链表,初始化为null)
	private Entry<K,V>[] table = null;
	
	/**
	 * 无参的构造函数,使用默认值(与带参数的构造形成门面模式)
	 */
	public MyHashMap(){
		this(defaultLength, defaultAddSizeFactory);
	}
	
	/**
	 * 带两个参数的构造函数
	 * @param length	外置的数组默认大小
	 * @param defaultAddSizeFactory	外置的扩容值
	 */
	public MyHashMap(int length, double defaultAddSizeFactory2){
		if(length < 0){
			throw new IllegalArgumentException("参数不能为负数:");
		}
		if(defaultAddSizeFactory <= 0 || Double.isNaN(defaultAddSizeFactory)){
			throw new IllegalArgumentException("扩容参数必须大于0");
		}
		defaultLength = length;
		defaultAddSizeFactory = defaultAddSizeFactory2;
		table = new Entry[defaultLength];
	}
	
	class Entry<K,V> implements MyMap.Entry<K, V>{
		
		K k;
		V v;
		// 指向下一个对象
		Entry<K,V> next = null;
		
		// 全参构造
		public Entry(K k, V v, Entry<K, V> next) {
			super();
			this.k = k;
			this.v = v;
			this.next = next;
		}

		@Override
		public K getKey() {
			return k;
		}

		@Override
		public V getValue() {
			return v;
		}
		
	}
	/**
	 * 存数据
	 * @param k
	 * @param v
	 */
	@Override
	public V put(K k, V v) {
		// 判定是否需要进行扩容
		if(userSize > defaultAddSizeFactory*defaultLength){
			up2Size();
		}
		int index = getIndex(k, table.length);
		Entry<K,V> entry = table[index];
		// 判定index的数组下标位置中是否已经存放过数据,并且存放数据是后放的数据离的最近
		if(entry == null){
			table[index] = new Entry(k,v,null);
		}else if(entry != null){
			// 若已经被占用则需要构建一个新的链表
			table[index] = new Entry(k,v,entry); 
		}
		return table[index].getValue();
	}

	/**
	 * 寻找数组位置的下标
	 * @param k
	 * @param length
	 * @return
	 */
	private int getIndex(K k, int length) {
		int i = length - 1;
		int index = hash(k.hashCode())&i;
		return index;
	}

	/**
	 * 自己的hash方法(进行大量的位移3后使数据更加散列)
	 * @param hashCode
	 * @return
	 */
	private int hash(int hashCode) {
		hashCode = hashCode^((hashCode>>>20)^(hashCode>>>12));
		return hashCode^((hashCode>>>7)^(hashCode>>>4));
	}

	/**
	 * 数组进行两倍扩容
	 * 1、创建两倍长度的新数组
	 * 2、
	 */
	private void up2Size() {
		Entry<K,V>[] newTable = new Entry[defaultLength*2];
		// 将原数组中的数组再次进行hash后放入新数组中
		againHash(newTable);
	}

	/**
	 * 将原数组中的数组再次进行hash后放入新数组中
	 * @param newTable
	 */
	private void againHash(MyHashMap<K, V>.Entry<K, V>[] newTable) {
		Map<String,String> map = new HashMap<String,String>();
		List<Entry<K,V>> entryList = new ArrayList<MyHashMap<K, V>.Entry<K,V>>();
		// 循环遍历数组并将数据加载值entryList中
		for(int i=0 ; i<table.length ; i++){
			if(table[i] == null){
				continue;
			}
			// 继续查找数组Entry对象
			foundEntryByNext(table[i], entryList);
		}
		// 设置entryList
		if(entryList == null){
			userSize = 0; 
			defaultLength = defaultLength*2;
			for(Entry<K,V> entry : entryList){
				if(entry.next != null){
					
				}
			}
		}
	}

	// 查找下一个元素并将数据存入list中
	private void foundEntryByNext(MyHashMap<K, V>.Entry<K, V> entry,
			List<MyHashMap<K, V>.Entry<K, V>> entryList) {
		if(entry != null && entry.next != null){
			entryList.add(entry);
			//进行递归查找
			foundEntryByNext(entry.next, entryList);
		}else{
			// 没有nest元素指向
			entryList.add(entry);
		}
	}

	/**
	 * 取数据
	 * @param k
	 */
	@Override
	public V get(K k) {
		int index = getIndex(k,table.length);
		if(table[index] == null){
			throw new NullPointerException();
		}
		return findValueByEqualKey(k, table[index]);
	}
	
	private V findValueByEqualKey(K k, MyHashMap<K, V>.Entry<K, V> entry) {
		if(k== entry.getKey() || k.equals(entry.getKey())){
			return entry.getValue();
		}else if(entry.next != null){
			return findValueByEqualKey(k,entry.next);
		}
		return null;
	}

	// 获取当时使用了多少个数组
	public int getUseSize(){
		return userSize;
	}

	// 测试
	public static void main(String[] args) {
		MyMap<String,String> map = new  MyHashMap<String,String>();
		for(int i=0 ; i<10000 ; i++ ){
			map.put(i+"", "测试"+i);
		}
		for(int i=0 ; i<10000 ; i++ ){
			System.out.println("第"+ i +"个对象值为: "+ map.get(i+"") );
		}
	}
}

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值