定义接口
public interface ExtMap<K,V> {
/**
* 向集合中插入数据
*/
public V put(K k, V v);
/**
* 根据k从Map集合中查询元素
*/
public V get(K k);
/**
* 获取集合中的元素的个数
*/
public int size();
/**
* 定义Map的内部类Entry,存放key和value
*/
interface Entry<K, V> {
K getKey();
V getValue();
V setValue(V value);
}
}
实现
/**
* 自定义实现HashMap
* @param <K>
* @param <V>
*/
public class ExtHashMap<K, V> implements ExtMap<K, V> {
/**
* 数组,下标由key的hashCode决定,值是链表实现的,默认是null,采取了懒加载的方式
*/
private Node<K, V>[] table = null;
/**
* 定义默认初始化数组的长度
*/
public static int DEFAULT_INITIAL_CAPACITY = 16;
/**
* 初始化负载因子,控制什么时候进行扩容
*/
public float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* 用来定义实际存储进map中的元素个数, 默认是0
*/
public int size;
@Override
public V get(K k) {
Node<K, V> node = getNode(table[getIndex(k, DEFAULT_INITIAL_CAPACITY)], k);
return node == null ? null : node.value;
}
@Override
public int size() {
return size;
}
/**
* 手写put方法
* @param key
* @param value
* @return
*/
@Override
public V put(K key, V value) {
// 1. 判断数组是否为空,如果为空,初始化
if (table == null) {
// 声明的数组是默认长短,默认长度16
table = new Node[DEFAULT_INITIAL_CAPACITY];
}
// 2. 判断是否需要进行扩容操作
// 当实际存储的大小(size)> 负载因子 * 数组的长度的时候,进行扩容,扩容后的大小是原来数组的二倍
// 因为数组的大小发生了改变,所以需要进行重新hash计算其下标
if (size > (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY)) {
// 扩容操作
resize();
}
// 3. 计算hash值指定下标的位置
int index = getIndex(key, DEFAULT_INITIAL_CAPACITY);
// 将数组当前下标的值赋值给一个新的node,可能该下标下已经有值,也可能该下标就是空
Node<K, V> node = table[index];
if (node == null) {
// 为空,表是该下标下没有元素,就是一个新的元素,没有发生hash冲突
// 下一个next没有值,赋值为空
node = new Node<>(key, value, null);
// 实际存储的长度加1
size ++ ;
} else {
// 已经发生了冲突
Node<K, V> newNode = node;
while (newNode != null) {
// 这个判断是判断是否已经有了这个key,只是进行覆盖操作
if (newNode.getKey().equals(key) || newNode.getKey() == key) {
return newNode.setValue(value);
} else {
// 直接走到下一个节点,要进行添加值
if (newNode.next == null) {
// 已经遍历到了最后一个
// 下一个节点赋值为当前的node节点,表示,新加入的元素做第一个元素
node = new Node<>(key, value, node);
size ++ ;
}
}
newNode = newNode.next;
}
}
// 将最新的节点放到table数组中的第一位
table[index] = node;
return null;
}
/**
* 根据node查询key相等的node
* @param node
* @param k
* @return
*/
private Node<K, V> getNode(Node<K, V> node, K k){
while (node != null){
if (node.getKey().equals(k)) {
return node;
}
node = node.next;
}
return null;
}
/**
* 根据key和数组的长度计算当前key应该存放在数组的哪个下标下面
* @param key
* @param length
* @return
*/
private int getIndex(K key, int length) {
int hashCode = key.hashCode();
return hashCode % length;
}
/**
* 数组的扩容操作
*/
private void resize(){
// 1. 生成新的table是之前的2倍
Node<K, V>[] newTable = new Node[DEFAULT_INITIAL_CAPACITY << 1];
// 2. 需要重新计算index的索引,因为数组的长度变了,所以index需要重新计算
// 遍历之前的数组,重新计算index
for (int i = 0; i < table.length; i++) {
Node<K, V> oldNode = table[i];
while (oldNode != null) {
// 为了尽快的进行垃圾回收,赋值为null
table[i] = null;
// 拿到key
K oldKey = oldNode.getKey();
// 重新计算index下标
int index = getIndex(oldKey, newTable.length);
// 得到当前节点的下一个节点
Node<K, V> oldNext = oldNode.next;
// 原来的node的下一个是最新的(原来的node oldNode该节点已经处理完毕)
// 下面两部的思路就是把最新的节点,作为头节点
oldNode.next = newTable[index];
newTable[index] = oldNode;
// 判断是否需要继续遍历循环
oldNode = oldNext;
}
}
// 3. 修改新的table数组
table = newTable;
DEFAULT_INITIAL_CAPACITY = newTable.length;
// 赋值为null---为了垃圾回收机制能够回收
newTable = null;
}
/**
* 定义Entry的具体实现,自己实现的列表, 其实就是链表每一个节点的信息
* @param <K>
* @param <V>
*/
class Node<K, V> implements Entry<K, V> {
/**
* 定义Map的key
*/
private K key;
/**
* 定义Map的value
*/
private V value;
/**
* 存放下一个节点的信息
*/
private Node<K, V> next;
/**
* 构造函数
* @param key
* @param value
* @param next
*/
public Node(K key, V value, Node<K, V> next) {
super();
this.key = key;
this.value = value;
this.next = next;
}
@Override
public K getKey(){
return this.key;
}
@Override
public V getValue(){
return this.value;
}
@Override
public V setValue(V value){
// 用来保存赋值前的值
V oldValue = value;
this.value = value;
return oldValue;
}
}
}
测试
public class Test {
public static void main(String[] args) {
ExtHashMap<String,String> extHashMap = new ExtHashMap<>();
extHashMap.put("张三","22");
System.out.println("--------张三===22--------"+extHashMap.get("张三"));
extHashMap.put("张三","27");
System.out.println("--------张三===27--------"+extHashMap.get("张三"));
extHashMap.put("李四","35");
System.out.println("--------李四===35--------"+extHashMap.get("李四"));
}
}