手写HashMap

定义接口:
/**
 * @author 孙一鸣 on 2020/3/4
 */
public interface ExtMap<K,V> {
    // 向集合中插入数据
    public V put(K k, V v);

    // 根据k 从Map集合中查询元素
    public V get(K k);

    // 获取集合元素个数
    public int size();

    // Entry的作用=== Node节点
    interface Entry<K, V> {
        K getKey();

        V getValue();

        V setValue(V value);
    }

}
定义变量

    // 1.定义table 存放HasMap 数组元素 默认是没有初始化容器 懒加载
    Node<K, V>[] table = null;
    // 2. 实际用到table 存储容量 大小
    int size;
    // 3.HashMap默认负载因子,负载因子越小,hash冲突机率越低, 根据每个链表的个数
    float DEFAULT_LOAD_FACTOR = 0.5f;
    // 4.table默认初始大小 16
    static int DEFAULT_INITIAL_CAPACITY = 16; // 16
定义Put方法:
 public V put(K key, V value) {

        // 1.判断table 数组大小是否为空(如果为空的情况下 ,做初始化操作)
        if (table == null) {
            table = new Node[DEFAULT_INITIAL_CAPACITY];
        }
        // 2. hashMap 扩容机制 为什么要扩容?扩容数组之后,有什么影响? hahsmap 中是从什么时候开始扩容
        // 实际存储大小=负载因子*初始容量=DEFAULT_LOAD_FACTOR0.75*DEFAULT_INITIAL_CAPACITY16=12
        // 如果size>12的时候就需要开始扩容数组,扩容数组大小之前两倍
        if (size > (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY)) {
            // 需要开始对table进行属数组扩容
            resize();
        }

        // 3.计算hash值指定下标位置
        int index = getIndex(key, DEFAULT_INITIAL_CAPACITY);
        Node<K, V> node = table[index];
        if (node == null) {
            // 没有发生hash冲突问题--- index冲突
            node = new Node<K, V>(key, value, null);
            size++;
        } else {

            Node<K, V> newNode = node;
            while (newNode != null) {
                // 已经发生hash冲突问题key 直接添加(冲突node)到前面了 不是往后面加
                if (newNode.getKey().equals(key) || newNode.getKey() == key) {
                    // hashCodoe 相同,而且equals 相等情况 说明是同一个对象 修改值
                    // node.value = value;
                    return newNode.setValue(value);
                } else {
                    // 继续添加,排在前面 hascode 取模余数相同 index 存放在链表 或者hashCode 相同但是对象不同
                    // 新的node 的next 原来的node
                    if (newNode.next == null) {
                        // 说明遍历到最后一个node ,添加node
                        node = new Node<K, V>(key, value, node);
                        size++;
                    }

                }
                newNode = newNode.next;
            }

        }
        table[index] = node;
        return null;
    }

定义扩容方法:
hashMap扩容思路:
  1. 新建数组,作为容器,大小为之前两倍
  2. 遍历数组
    • 得到数组中每一个节点oldNode = table[i] ,由于Hash冲突 节点后面还有节点

    • 遍历oldNode

      • table[i] 数据已经被oldNode保存,令值为null,GC回收
      • 因为数组长度改变,重新计算index
      • 定义OldNext储存当前节点oldNode的下一节点;【当前节点的下一个节点值要改变,因此储存起来
      • 当前节点的下一个节点 赋值为新数组newTable【index】【如果没有Hash冲突,当前节点的下一个节点的值为null
      • 新数组newTable【index】 等于 oldNode
      • oldNode = OldNext【指针移动】
    // 对table进行扩容
    private void resize() {

        // 1.生成新的table 是之前的两倍的大小 DEFAULT_INITIAL_CAPACITY*2
        Node<K, V>[] newTable = new Node[DEFAULT_INITIAL_CAPACITY << 1];
        // 2.重新计算index索引,存放在新的table里面
        for (int i = 0; i < table.length; i++) {
            // 存放在之前的table 原来的node
            Node<K, V> oldNode = table[i];
            // a 的index=1 b 的index=1
            // a ##
            while (oldNode != null) {
                table[i] = null;// 赋值为null---为了垃圾回收机制能够回收 将之前的node删除
                // 存放在之前的table 原来的node key
                K oldK = oldNode.key;
                // 重新计算index
                int index = getIndex(oldK, newTable.length);
                // 存放在之前的table 原来的node next
                if (oldK.equals("22号") || oldK.equals("66号")) {
                    System.out.println("日志记录");
                }
                Node<K, V> oldNext = oldNode.next;
                // 如果ndex 下标在新newTable发生相同的index时候,以链表进行存储 //
                // 原来的node的下一个是最新的(原来的node存放下新的node下一个)
                oldNode.next = newTable[index];
                // 将之前的node赋值给 newTable[index]
                newTable[index] = oldNode;
                // 判断是否继续循环遍历
                oldNode = oldNext;

            }
        }
        // 3.将newtable赋值给老的table
        table = newTable;
        DEFAULT_INITIAL_CAPACITY = newTable.length;
        newTable = null;/// 赋值为null---为了垃圾回收机制能够回收

    }
举例说明:
根据key得到hash值:

    public int getIndex(K k, int length) {
        int hashCode = k.hashCode();
        // System.out.println("k:" + k + ",hashCode=" + hashCode);
        int index = hashCode % length;
        return index;
    }
get方法:
    public V get(K k) {

        Node<K, V> node = getNode(table[getIndex(k, DEFAULT_INITIAL_CAPACITY)], k);
        return node == null ? null : node.value;
    }

    public Node<K, V> getNode(Node<K, V> node, K k) {
        while (node != null) {
            if (node.getKey().equals(k)) {
                return node;
            }
            node = node.next;
        }
        return null;
    }

所有代码:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值