我们提到过,在JDK7的时候,HashMap底层使用的是数组加链表。
那么我们来模仿它的底层来写。在我们之前提到过,他实现了接口Map,HashMap底层是使用Node来存储键值对的。
JDK7状态下的HashMap
我们先使用list集合来存储数据。
创建ArrayListHashMap.java
利用private final ArrayList<Node<K,V>> arrayList = new ArrayList<>();
来存储多组键值对
package com.hashmap;
import java.util.ArrayList;
import java.util.HashMap;
/**
* @author 龙小虬
* @date 2021/3/25 20:05
* 缺点:查询速度过慢 有重复数据
*/
public class ArrayListHashMap<K,V>{
private final ArrayList<Node<K,V>> arrayList = new ArrayList<>();
public V get(Object key) {
for (Node<K,V> node : arrayList) {
// 如果有数据则返回
if(node.key.equals(key)){
return node.value;
}
}
return null;
}
public V put(K key, V value) {
Node<K,V> node = new Node<K,V>(key,value);
arrayList.add(node);
return value;
}
static class Node<K,V>{
private final K key;
private final V value;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
}
public static void main(String[] args) {
ArrayListHashMap<Object, Object> arrayListHashMap = new ArrayListHashMap<>();
arrayListHashMap.put("a","a");
arrayListHashMap.put(97,97);
System.out.println(arrayListHashMap.get(97));
}
}
这样我们就写出来ArrayList的键值对数据。可是我们看到Get()方法,可以看到,他的复杂度足足达到了O(N),假如我们需要存入几百万个数据,那么我们是不是查找一个数据,最慢可能就是把这几百万个数据全部遍历一遍???
最后,就加入了数组。
利用每个数据的Hash值,存入数组中。
这里因为涉及到了一个规范,在阿里的java开发手册中。有强调
1. 【强制】关于 hashCode 和 equals 的处理,遵循如下规则:
1) 只要覆写 equals,就必须覆写 hashCode。
2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须覆
写这两个方法。
3) 如果自定义对象作为 Map 的键,那么必须覆写 hashCode 和 equals。
说明:String 已覆写 hashCode 和 equals 方法,所以我们可以愉快地使用 String 对象作为 key 来使用。
在后面的说明也提到了,String已经对 hashCode 和 equals进行了重写。所以我们后面模仿的HashMap的键值对类型就使用String吧。
前者使用Arraylist是因为它类似于链表,所以我们使用了它,现在我们就使用链表吧。
因为我们要使用数组+链表来实现。所以,我们需要使用数组来存储链表来实现这样一个要求。
创建ExtHashMap.java
使用private Node[] objects = new Node[10000];
来存储链表。
代码如下
package com.hashmap;
/**
* @author 龙小虬
* @date 2021/3/30 20:22
*/
public class ExtHashMap<K, V> {
private Node[] objects = new Node[10000];
static class Node<K, V> {
public K k;
public V v;
Node<K, V> next;
public Node(K k, V v) {
this.k = k;
this.v = v;
}
public void setV(V v) {
this.v = v;
}
}
public void put(K k, V v) {
int index = k == null ? 0 : k.hashCode() % objects.length;
Node<K, V> oldNode = objects[index];
// 判断是否存在
if (oldNode == null) {
objects[index] = new Node<>(k, v);
} else {
// 发生hash碰撞则存放到链表后面
for (Node<K, V> oldNodeMap = objects[index]; oldNodeMap != null; oldNodeMap = oldNodeMap.next) {
if (oldNodeMap.k.equals(k)) {
oldNodeMap.setV(v);
}
}
oldNode.next = new Node<>(k, v);
}
}
public V get(K k) {
// if (k == null) {
// for (Entry<K, V> oldEntry = objects[0]; oldEntry != null; oldEntry = oldEntry.next) {
// if (oldEntry.k.equals(k)) {
// return oldEntry.v;
// }
// }
// }
int index = k == null ? 0 : k.hashCode() % objects.length;
for (Node<K, V> oldEntry = objects[index]; oldEntry != null; oldEntry = oldEntry.next) {
if (oldEntry.k == null || oldEntry.k.equals(k)) {
return oldEntry.v;
}
}
return null;
}
public static void main(String[] args) {
ExtHashMap<Object, String> hashMap = new ExtHashMap<>();
hashMap.put("a", "1212");
hashMap.put(97, "121212");
hashMap.put(null, "null");
System.out.println(hashMap.get("a"));
System.out.println(hashMap.get(97));
System.out.println(hashMap.get(null));
}
}
在代码中使用了特判。判断key==null,为何呢?在前面我们提到过,HashMap与HashTable之间的区别。HashMap是允许key为null的,并且key为null的时候,此键值对存放在数组下表为0的位置。
在put()中,需要进行键值判断,是否覆盖之前一模一样的键值。
JDK8状态下的HashMap
后续更新