简要实现java中的HashMap

实现的是JDK8以前的版本,也就是“散列+链表”结构。JDK8以后当链表长度大于8后会转换为树。

HashMap继承于Map接口,允许key、value值为null,key也可以相同(相同时值覆盖),线程不安全,效率高。底层的数据结构是“散列+链表”,如下图:
在这里插入图片描述
每个key对应一个 int 型hashcode,通过散列函数将hashcode均匀的映射到散列数组table,常用的散列函数是取余操作,比如数组长度为16时散列函数可以是用hashcode对16取余,这样结果就在0~15之间,对应数组的每个下标。因为 int 型hashcode值范围必定远大于table数组长度,所以存在两个不同的hashcode映射到同一个数组位置,这里用到的处理冲突的方法是链地址法:即把相同散列值的元素放在同一个单链表中,同时用数组存放各单链表的头结点。

下面是根据这一思想对HashMap中主要功能的简单实现。

class Node<K, V> {
	int hash;
	K key;
	V value;
	Node<K, V> next; 											// 指向单链表的下一个节点
}


class MyHashMap<K, V> {
	Node<K, V>[] table; 										// 散列数组
	int size; 													// 存放键值对的个数

	@SuppressWarnings("unchecked")
	public MyHashMap() {
		table = new Node[16]; 									// 长度一般取2的整数次幂,默认16
	}

	// 根据hashcode返回散列值也就是散列数组下标
	private int myHash(int v, int length) {
		// 也可以return v&(length-1); v&(length-1)=v%(length),但与运算快于取余运算
		return v % (length);
	}

	// 键值对存放
	public void put(K key, V value) {
		Node<K, V> newNode = new Node<>();
		newNode.hash = myHash(key.hashCode(), table.length); 	// 获取散列值也就是散列数组下标
		newNode.key = key;
		newNode.value = value;

		Node<K, V> curNode = table[newNode.hash]; 				// 获取散列数组下标位置元素
		if (curNode == null) {
			table[newNode.hash] = newNode; 						// 数组位置空直接放进去
			size++;
		} else {
			Node<K, V> lastNode = null; 						// 表示链表中最后一个结点
			boolean keyRepeat = false; 							// 标记key是否重复
			while (curNode != null) {
				if (curNode.key.equals(newNode.key)) {
					keyRepeat = true;
					curNode.value = newNode.value; 				// 重复时值覆盖
					break;
				} else {
					lastNode = curNode; 						// 否则获取到单链表最后一个结点
					curNode = curNode.next;
				}
			}
			if (!keyRepeat) {
				lastNode.next = newNode; 						// 如果键没有重复最后一个结点指向新结点
				size++;
			}
		}
	}

	// 根据键获取值,没有这个键返回null
	public V get(K key) {
		int hash = myHash(key.hashCode(), table.length);
		Node<K, V> curNode = table[hash];
		V value = null;
		while (curNode != null) {
			if (curNode.key.equals(key)) {
				value = curNode.value;
				break;
			}
			curNode = curNode.next;
		}
		return value;
	}

	// 根据键删除键值对
	public void remove(K key) {
		int hash = myHash(key.hashCode(), table.length); 		// 获取hash值也就是散列数组地址

		Node<K, V> curNode = table[hash]; 						// 获取散列数组该位置元素
		if (curNode == null) {
			System.out.println("没有找到该键!"); 				// 元素为空表示没有这样的key值
		} else {
			int ordNum = 1; 									// ordNum表示链表中key值的位置序号
			Node<K, V> preNode = null; 							// 因为单链表所以需要保存key值处上一个位置的元素
			while (curNode != null) {
				if (curNode.key.equals(key)) {
					break;
				} else {
					preNode = curNode;
					curNode = curNode.next;
					ordNum++;
				}
			}
			if (curNode == null) { 								// 遍历到链表末尾没有找到
				System.out.println("没有找到该键!");
			} else if (ordNum == 1) { 							// 在链表头结点用第二个节点覆盖头结点
				System.out.println("删除成功!删除元素是---" + table[hash].key + ":" + table[hash].value);
				table[hash] = table[hash].next;
				size--;
			} else { 											// 存在且不在头结点
				preNode.next = curNode.next; 					// 当前结点上一个元素指向当前结点下一个元素
				System.out.println("删除成功!删除元素是---" + curNode.key + ":" + curNode.value);
				size--;
				curNode = null; 								// 释放空间
			}
		}
	}

	// 重写toString方法打印内容
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder("{");
		for (int i = 0; i < table.length; i++) {
			Node<K, V> curNode = table[i];
			while (curNode != null) {
				sb.append(curNode.key + ":" + curNode.value + ",");
				curNode = curNode.next;
			}
		}
		if (sb.charAt(sb.length() - 1) == ',') {
			sb.setCharAt(sb.length() - 1, '}');
		} else {
			sb.append("}");
		}
		return sb.toString();
	}
}

// 测试
public class TestMyHashMap {
	public static void main(String[] args) {
		MyHashMap<Integer, String> map = new MyHashMap<>();
		map.put(10, "小王1");
		System.out.println(map);
		map.put(10, "小王2");
		System.out.println(map);
		map.put(53, "小张1");
		map.put(69, "小张2");
		map.put(85, "小张3");
		System.out.println(map);
		System.out.println(map.get(53));
		map.remove(53);
		System.out.println(map);
		System.out.println("长度" + map.size);
	}
}

测试结果
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值