1. 虽然说Java中已经为我们实现了HashMap,但是我们可以自己写一个自定义的HashMap,这样可以锻炼我们封装数据结构的能力
2. 首先需要写一个接口IHashMap,接口中声明了对数据操作的方法
import java.util.Iterator;
public interface IHashMap <K, V>{
//清除所有的键值对
void clear();
//value是否存在
boolean containsValue(Object value);
//key是否存在
boolean containsKey(K key);
//根据key获得value
V get(K key);
//map是否为空
boolean isEmpty();
//所有的key组成的数组
IHashSet<K> keySet();
//存入键值对
void put(K key, V value);
//把另外一个map中所有键值对存入到当前的map中
void putAll(IHashMap<? extends K, ? extends V>map);
//根据key删除一个键值对
V remove(K key);
//键值对的个数
int size();
//所有的value组成的数据
V[] values();
Iterator<MyHashMap.Node> iterator();
}
首先我们应该有存放数据的容器,所以应该有一个大的桶来装这些数据,这里我们使用数组来模仿和这个桶,桶中应该存放的是一个个的节点,因为涉及到了键与值多个属性所以我们可以创建内部的私有类来封装键-值对
实现HashMap首先要根据它的思想去实现,其中使用到了哈希算法,目的是为了更多的数据均匀或者等概率地分布在各个容器中
以便以后在查询的时候能够以更快的速度去查询,所以我们需要首先要解决哈希函数的问题
这里我们可以使用直接取余数的方法,这种哈希函数是比较简便的,我们可以自定义哈希表的长度为len,Java中的每个对象都是有它自己的HashCode的,所以我们可以利用对象本身的HashCode然后对哈希表的长度取余数,那么这些数据就分布到各个桶中了
哈希函数解决了,但是存在着冲突的问题,因为取余数之后那么可能好几个数据落在一个桶中,所以这个时候就造成了数据的冲突,我们可以采用拉链法来解决这个冲突,即使用链表将落在一个桶中的数据连接起来,这样便解决了冲突的问题
因为数组中存放的是键-值对的节点,每个小桶存放的是一个链表,所以我们在声明数组的时候可以声明为数组对象
private Node[] buckets = new Node[16]
思路清晰之后我们就可以接口的实现类:MyHashMap
3. 具体的代码如下:
import java.util.Iterator;
//K代表键, V代表值<K, V>是泛型
public class MyHashMap<K, V> implements IHashMap<K, V>{
private int length = 16;
//需要在节点处加上泛型这样创建数组的时候才不会报错
private Node[] buckets = new Node[16];//桶,桶中存放链表
private int size;
//声明成public方便外界进行访问
public class Node<K, V>{
private Node<K, V> next;//next不允许外界进行访问
public K key;
public V value;
public Node(K key, V value) {
super();
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "Node [key=" + key + ", value=" + value + "]";
}
}
//清空buckets中的内容
@Override
public void clear() {
for(int i = 0; i < buckets.length; i++){
buckets[i] = null;
}
}
@Override
public boolean containsValue(Object value) {
for(int i = 0; i < buckets.length; i++){
if(buckets[i] != null){
Node<K, V> p = buckets[i];
while(p != null){
if(p.value.equals(value)){
return true;
}
p = p.next;
}
}
}
return false;
}
@Override
public boolean containsKey(K key) {
int index = hash((K) key);
if(buckets[index] == null)return false;
Node<K, V> p = buckets[index];//相当于在链表中找key
while(p != null){
K k1 = p.key;
//借用用java机制,hasCode和equals都来自于Object,所以用户可以改写这两个方法
//来指定对象相等的规则
if(k1 == key || k1.hashCode() == key.hashCode() && k1.equals(key)){
return true;
}
p = p.next;
}
return false;
}
@Override
public V get(K key) {
int index = hash(key);
if(buckets[index] == null) return null;
Node<K, V> p = buckets[index];
while(p != null){
K key1 = p.key;
if(key == key1 || (key.hashCode() == key1.hashCode() && key.equals(key1))){
return p.value;
}
p = p.next;
}
return null;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public MyHashSet<K> keySet() {
MyHashSet<K> set = new MyHashSet<K>();
for(int i = 0; i < buckets.length; i++){
if(buckets[i] != null){
Node<K, V> p = buckets[i];
while(p != null){
set.add(p.key);
p = p.next;
}
}
}
return set;
}
@Override
public void put(K key, V value) {
Node<K, V> node = new Node<>(key, value);
//给定一个key论你给定什么样的数字我们都给你一个0~15之间的哈希值
int index = hash(key);
//桶中没有东西
if(buckets[index] == null){
buckets[index] = node;
size++;
}else{
//找到链表表头
Node<K, V> p = buckets[index];
while(p!= null){
K k1 = p.key;
//对重复元素的处理
//需要清除==与.equals()的区别
if(key == k1 || key.hashCode() == k1.hashCode() && key.equals(k1)){
p.value = value;//存在相同的key,则更新value
break;
}
//这里需要特别处理才不会造成空指针的异常
if(p.next == null){
p.next = node;
size++;
break;
}
p = p.next;
}
}
}
private int hash(K key) {
//哈希函数
return key.hashCode() % length;
}
@Override
public void putAll(IHashMap<? extends K, ? extends V> map) {
}
@Override
public V remove(K key) {
//相当于删除链表的节点
//先定位key对应在哪个桶
int index = hash(key);
if(buckets[index] == null) return null;
Node<K, V> p = buckets[index];
Node<K, V> pre = buckets[index];
while(p != null){
//这里特别要注意删除的假如是头结点的话我们需要特别的处理
K key1 = p.key;
if(key1 == key || (key1.hashCode() == key.hashCode() && key1.equals(key1))){
if(p == pre){
//假如是头节点那么头结点直接往下移动
buckets[index] = p.next;
}else{
pre.next = p.next;
}
size--;
return p.value;
}
pre = p;
p = p.next;
}
return null;
}
@Override
public int size() {
return size;
}
@Override
public V[] values() {
return null;
}
//重写toString方法
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < buckets.length; i++){
if(buckets[i] != null){
Node<K, V> p = buckets[i];
while(p != null){
sb.append("(" + p.key + "," + p.value + ")");
p = p.next;
}
}
}
return sb.toString();
}
//内部类
private class MapIterator implements Iterator{
int i = 0;
Node<K, V> p = buckets[0];
@Override
public boolean hasNext() {
while(i < length && p == null){
i++;
if(i == length){
p = null;
}
else{
p = buckets[i];
}
}
//执行到这里说明i指向的是一个非空的桶
return p != null;
}
@Override
public Object next() {
Node res = p;
p = p.next;
return res;
}
}
//增加这个方法是为了更好地操作HashSet
@Override
public Iterator<Node> iterator() {
return new MapIterator();
}
}