HashMap源码分析------手写HashMap

我们提到过,在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
后续更新

猜你喜欢

转载自blog.csdn.net/weixin_43911969/article/details/115335872