package com.fzq.hashmaptest;
/**
* 手写HashMap的get方法和put方法
*/
public class MyHashMap<K, V> {
private Node<K, V>[] table;
private int size;
static class Node<K, V> {
int hash;
K key;
V value;
Node<K, V> next;
/**
* 构造一个结点对象
*
* @param hash 哈希值
* @param key 键
* @param value 值
* @param next 下一个结点的地址
*/
public Node(int hash, K key, V value, Node<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
/**
* 重写Node方法的toString方法
*
* @return 返回键值对
*/
@Override
public String toString() {
return "Node{" + "key=" + key + ", value=" + value + '}';
}
}
public int size() {
return size;
}
@SuppressWarnings("unchecked")
public MyHashMap() {
this.table = new Node[16];
}
public MyHashMap(Node<K, V>[] table, int size) {
this.table = table;
this.size = size;
}
/**
* 重写toString方法,直接输出Map集合是会调用
*
* @return ""
*/
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < table.length; i++) {
Node<K, V> node = table[i];
while (null != node) {
stringBuilder.append(node.toString());
stringBuilder.append("\n");
node = node.next;
}
}
return stringBuilder.toString();
}
/**
* 向MyHashMap添加一个键值对
*
* @param key 键
* @param value 值
* @return value,如果Key重复,则返回oldValue,如果Key不重复,则返回newValue
*/
public V put(K key, V value) {
// 【第一步】:处理key为null的情况,如果添加键值对的key就是null,则将该键值对存储到table数组索引为0的位置
if (key == null) {
return putForNullKey(value);
}
// 【第二步】:获得key对象的哈希值,如果添加的键值对不是null,则调用key的hashcode方法,获得key的哈希值
int hash = key.hashCode();
// 【第三步】:获得键值对的存储位置,因为获得的哈希值在数组合法索引范围之外,因此我们就需要将获取的哈希值转化为[0,数组长度-1]范围的整数,
// 那么可以通过取模法来实现,也就是通过“哈希值 % 数组长度”来获得索引位置(i)。
int index = Math.abs(hash % table.length);
Node<K, V> node = table[index];
if (null == node) {
table[index] = new Node<K, V>(hash, key, value, null);
size++;
return value;
}
// 【第四步】:将键值对添加到table数组中
// 当table[i]返回结果为null时,则键值对封装成Node对象并存入到table[i]的位置。
// 当table[i]返回结果不为null时,则意味着table[i]存储的是单链表。我们首先遍历单链表,如果遍历出来节点的key和添加键值对的key相同
// 那么久执行覆盖操作:如果遍历出来节点的key和添加键值对的key都不同,则将键值对封装为Node对象并插入到单链表末尾
Node<K, V> prev = null;
while (null != node) {
if (node.key.equals(key)) {
V oldValue = node.value;
node.value = value;
return oldValue;
}
prev = node;
node = node.next;
}
prev.next = new Node<K, V>(hash, key, value, null);
size++;
return value;
}
/**
* 如果添加键值对的key就是null,则将该键值对存储到table数组索引为0的位置
*
* @param value 值
* @return 返回值
*/
private V putForNullKey(V value) {
Node<K, V> node = table[0];
// 如果table数值索引为0的位置没有数据,则新增一条数据
if (null == node) {
table[0] = new Node<K, V>(0, null, value, null);
size++;
return value;
}
Node<K, V> prev = null;
while (null != node) {
if (null == node.key) {
V oldValue = node.value;
node.value = value;
return oldValue;
}
prev = node;
node = node.next;
}
prev.next = new Node<K, V>(0, null, value, null);
size++;
return value;
}
/**
* 通过key获取value
*
* @param key 键
* @return 值
*/
public V get(K key) {
/*
第一步:处理key为null的情况
如果查询的key就是null,则在table数组索引为0的位置查询
*/
if (null == key) {
Node<K, V> node = table[0];
if (null == node){
return null;
}
while (null != node){
if (node.key == null){
return node.value;
}
node = node.next;
}
}
/*
第二步:获得key对象的哈希值
如果查询的key不是null,则调用key的hashCode方法,获得key的哈希值
*/
int hash = key.hashCode();
/*
第三步:获得键值对的存储位置
因为获得的哈希值在数组合法索引范围之外,因此我们就需要将获得的哈希值转化为[0,数组长度-1]范围的整数,
那么可以通过取模法来实现,也就是通过"哈希值 % 数组长度"来获得索引位置(i)
*/
int index = Math.abs(hash % table.length);
/*
第四步:遍历单链表,根据key获得value值
如果table[i]返回的结果为null,则证明单链表不存在,直接返回null
如果table[i]返回的结果不为null,则证明单链表存在,那么久遍历整个单链表,如果遍历出来的结点的key和查询的key相同,那么就返回遍历出来的结点的value值
如果整个单链表遍历完毕,则遍历出来结点的key和查询的key都不相等,那么就证明查询key在链表中不存在,返回null
*/
Node<K, V> node = table[index];
if (null == node){
return null;
}
while (null != node){
if (node.key.equals(key)){
return node.value;
}
node = node.next;
}
return null;
}
}
Java自定义编写HashMap的get和put方法
于 2024-04-26 02:39:45 首次发布