手写代码:HashMap

一、简介

1、原理解析

Entry[ ] table 就是HashMap的核心数组结构,我们也称之为“位桶数组”;
一个Entry对象存储了:

  1. key:键对象 value:值对象

  2. next:下一个节点

  3. hash: 键对象的hash值

显然每一个Entry对象就是一个单向链表结构,我们使用图形表示一个Entry对象的典型示意:
在这里插入图片描述
然后,我们画出Entry[]数组的结构(这也是HashMap的结构):

在这里插入图片描述
存储过程

在这里插入图片描述

2、单个方法原理解析

1、根据key的hash值,确定存储的位置(取模)

根据key的hash值,取模确定一个值(值小于桶),实现随机存储在桶中;

2、存储元素(key-value)

创建存储对象,添加值到Node元素对象中,存的过程分为三种情况。

(1): 获取的桶元素为空,直接将当前元素存进去即可;

(2): 遍历当前桶中元素,如果有相同key,则把value值进行替换;

(3): 遍历当前桶中元素,如果没有重复的key,则把值添加到最后;

3、重写toString方法

创建StringBuilder进行存储,遍历桶,再将桶中的链表的进行遍历,取出其中存储的value的值,进行返回;

4、get方法

根据1、方法获取key特定的hash值,找到指定的桶,遍历其中的链表,根据key的值,取出value值返回即可。

二、实现的代码

Node

public class Node<K, V> {

    public int hash;    //key的hash值
    public K key;       //key键的的值
    public V value;     //存储的value值
    public Node next;   //下一个元素的地址值
}

HashMap

public class MyHashMap<K, V> {

    Node[] table;    //位桶数组
    int size;       //存放的键值对的个数

    public MyHashMap() {
        table = new Node[16];
    }

    public MyHashMap(Node[] table, int size) {
        this.table = table;
        this.size = size;
    }

    //    1、根据key的hash值,确定存储的位置(取模)
    private int myHash(int v, int length) {
        return v & (length - 1);
    }

    //    2、存储元素(key-value)
    public void put(K key, V value) {
//        -1、创建存储的对象,并把值放进去
        Node newNode = new Node();
        newNode.hash = myHash(key.hashCode(), table.length);
        newNode.key = key;
        newNode.value = value;
        newNode.next = null;

//        -2、将有值的对象,放进hashMap对象的存储位置中
        Node temp = table[newNode.hash];

        Node iterLast = null;     //正在遍历的最后一个元素
        boolean keyRepeat = false;

        if (temp == null) {
            //1、此处数组元素为空,则直接将节点放进去
            table[newNode.hash] = newNode;
            size++;
        } else {
//           2、此处数组元素不为空。则遍历对应链表
            while (temp != null) {
                if (temp.key.equals(key)) {
                    keyRepeat = true;
                    temp.value = value;  //只是覆盖value的值即可。其他的(hash,key,next)值保持不变
                    break;
                } else {
//                     key不重复,则遍历下一个
                    iterLast = temp;
                    temp = temp.next;
                }
            }
//             3、没有发生key重复的情况,则添加到链表的最后
            if (!keyRepeat) {
                iterLast.next = newNode;
                size++;
            }
        }
    }


    //    3、重写toString方法
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("{");

//        遍历bucket数组
        for (int i = 0; i < table.length; i++) {
            Node temp = table[i];

//            遍历链表
            while (temp != null) {
                sb.append(temp.key + ":" + temp.value);
                temp = temp.next;
            }
        }
        sb.setCharAt(sb.length() - 1, '}');
        return sb.toString();
    }


    //    4、get方法
    public V get(K key) {
        int hash = myHash(key.hashCode(), table.length);
        V value = null;

        if (table[hash] != null) {
            Node temp = table[hash];

            while (temp != null) {
//                相等:则说明找到了键值对,返回相应的value值
                if (key.equals(temp.key)) {
                    value = (V) temp.value;
                    break;
                } else {
                    temp = temp.next;
                }
            }
        }
        return value;
    }
}
posted @ 2020-02-11 14:59  ah_lydms  阅读(140)  评论(0编辑  收藏  举报