哈希表的实现
使用拉链法来实现哈希表,也就是说我这里哈希表的底层数据结构使用的是 数组和链表来实现
为了简化操作,这里我们链表就直接使用List接口的实现类LinkedList
实现MyHashMap的代码
实际上在实现的时候有一个较大的感受就是,Java很多代码已经整合的非常好了
-
比如说在通过key来计算所谓的哈希值的时候,实际上我们只需要调用
hashCode()这个方法即可,因为Java中任何一个类(包括我们自定义的类)都是Object类的子类,Object类中就有hashCode()方法- 那么不管是后续的子类,是否重写了
hashCode()方法。 反正调用就完事儿了
- 那么不管是后续的子类,是否重写了
package com.nylonmin.mapDemo;
import java.util.LinkedList;
import java.util.List;
@SuppressWarnings("all")
public class MyHashMap<K,V> {
private int size; // 记录当前存储的键值对的个数
private LinkedList<Node<K,V>>[] table;
private static final int DEFAULT_CAPACITY = 16;
//阈值比率
private static final double THRESHOLD_RATE = 0.75;
/**
* 静态内部类, 定义节点
* @param <K>
* @param <V>
*/
private static class Node<K,V>{
K key;
V value;
public Node(K key,V value){
this.key= key;
this.value=value;
}
}
/**
* 无参构造函数,默认创建长度为16大小的数组
*/
public MyHashMap(){
this(DEFAULT_CAPACITY);
}
/**
* 有参构造函数,创建容量为指定的capacity大小的数组
* @param capacity
*/
public MyHashMap(int capacity){
if(capacity<0 || capacity >Integer.MAX_VALUE){
throw new IllegalArgumentException("初始化哈希表有误,容量输入有误");
}
this.size = 0;
table = (LinkedList<Node<K,V>>[])new LinkedList[capacity];
for(int i=0;i<table.length;i++){
table[i] = new LinkedList<Node<K,V>>();
}
}
/**
* 计算当前key对应的哈希值
* 参照hashMap底层来做
* @return
*/
private int hash(K key){
int h;
return (h=key.hashCode()) ^ h>>>16;
}
/**
* 根据键key来查询对应的值value
* @param key
* @return
*/
public V get(K key){
Node<K, V> node = this.getNode(key);
if(node == null){
return null;
}
return node.value;
}
/**
* 通过key类获取对应的节点
* 用private修饰,达到后续增加 等功能能够复用 ,减少代码的重复率
* @param key
* @return
*/
private Node<K,V> getNode(K key){
if (key == null) {
throw new IllegalArgumentException("key is null");
}
LinkedList<Node<K,V>> list = new LinkedList<>();
int n = table.length;
list = this.table[hash(key) & (n-1)];
for(Node<K,V> node : list){
if (node.key.equals(key)){
return node;
}
}
return null;
}
/**
* 往map中增加元素
* @param key 键
* @param value 值
* @return 返回是否增加成功
*/
public boolean put(K key,V value){
Node<K, V> node = getNode(key);
//如果已经查到了有对应的key 那么直接修改node对应的value即可
if(node != null){
node.value=value;
return true;
}
//如果没有查到 那么就要检查是否需要扩容
// TODO 检查是否需要扩容
if(size+1> THRESHOLD_RATE*table.length){
resize(table.length*2);
}
LinkedList<Node<K,V>> list = new LinkedList<>();
list = table[hash(key) & (table.length-1)];
//如果当前哈希表中没有对应的key对应的node,那么直接插入
Node node1 = (Node<K,V>) new Node(key, value);
list.add(node1);
size++;
return true;
}
/**
* 根据key来移除node
* @param key
* @return
*/
public Node<K,V> remove(K key){
Node<K, V> node = getNode(key);
//如果哈希表中不存在这个key对应的node
if(node == null){
return null;
}
LinkedList<Node<K,V>> list = new LinkedList<>();
list = table[hash(key) & (table.length-1)];
list.remove(node);
size--;
return node;
}
/**
* 将table的容量改变为指定的capacity大小
* HashMap的底层代码是扩容到大于等于当前容量两倍的 最小2的幂次方
* 这里我们就简单化处理扩容到原容量两倍就行
* @param capacity
*/
private void resize(int newCapacity){
//不能让newCapacity超出Integer的最大长度
newCapacity = Math.min(newCapacity,Integer.MAX_VALUE);
MyHashMap<K,V> newMap = new MyHashMap<>(newCapacity);
//把原来的map中的Node放入到新的map中
for(int i=0;i<table.length;i++){
LinkedList<Node<K,V>> list = table[i];
for(Node<K,V> node :list){
newMap.put(node.key,node.value);
}
}
//把HashMap的底层table数组给替换掉
this.table = newMap.table;
}
/**
* 简单工具类
*/
public int size(){
return size;
}
public List<K> keys(){
List<K> result = new LinkedList<>();
for(LinkedList<Node<K,V>> list :table){
for(Node<K,V> node :list){
result.add(node.key);
}
}
return result;
}
}
测试代码
package com.nylonmin.mapDemo;
@SuppressWarnings("all")
public class MapDemoTest {
public static void main(String[] args) {
MyHashMap<Integer, Integer> map = new MyHashMap<>();
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
System.out.println(map.get(1)); // 1
System.out.println(map.get(2)); // 2
map.put(1, 100);
System.out.println(map.get(1)); // 100
map.remove(2);
System.out.println(map.get(2)); // null
// [1, 3](顺序可能不同)
System.out.println(map.keys());
map.remove(1);
map.remove(2);
map.remove(3);
System.out.println(map.get(1)); // null
//下面是用来测试是否会扩容
for(int i=0;i<20;i++){
map.put(i,1);
}
}
}