源码03_手写简易版HashMap(JDK1.7)带扩容功能

这篇博客详细介绍了如何实现一个自定义的HashMap,包括接口ExtMap的定义和ExtHashMap类的实现,涵盖了put、get、size等基本操作,并且包含内部类Entry的定义以及扩容机制的实现。
摘要由CSDN通过智能技术生成

定义接口

public interface ExtMap<K,V> {
    /**
     * 向集合中插入数据
     */
    public V put(K k, V v);

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

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

    /**
     * 定义Map的内部类Entry,存放key和value
     */
    interface Entry<K, V> {
        K getKey();

        V getValue();

        V setValue(V value);
    }
}

实现

/**
 * 自定义实现HashMap
 * @param <K>
 * @param <V>
 */
public class ExtHashMap<K, V> implements ExtMap<K, V> {

    /**
     * 数组,下标由key的hashCode决定,值是链表实现的,默认是null,采取了懒加载的方式
     */
    private Node<K, V>[] table = null;

    /**
     * 定义默认初始化数组的长度
     */
    public static int DEFAULT_INITIAL_CAPACITY = 16;

    /**
     * 初始化负载因子,控制什么时候进行扩容
     */
    public float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * 用来定义实际存储进map中的元素个数, 默认是0
     */
    public int size;

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

    @Override
    public int size() {
        return size;
    }

    /**
     * 手写put方法
     * @param key
     * @param value
     * @return
     */
    @Override
    public V put(K key, V value) {
        // 1. 判断数组是否为空,如果为空,初始化
        if (table == null) {
            // 声明的数组是默认长短,默认长度16
            table = new Node[DEFAULT_INITIAL_CAPACITY];
        }
        // 2. 判断是否需要进行扩容操作
        // 当实际存储的大小(size)> 负载因子 * 数组的长度的时候,进行扩容,扩容后的大小是原来数组的二倍
        // 因为数组的大小发生了改变,所以需要进行重新hash计算其下标
        if (size > (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY)) {
            // 扩容操作
            resize();
        }

        // 3. 计算hash值指定下标的位置
        int index = getIndex(key, DEFAULT_INITIAL_CAPACITY);
        // 将数组当前下标的值赋值给一个新的node,可能该下标下已经有值,也可能该下标就是空
        Node<K, V> node = table[index];
        if (node == null) {
            // 为空,表是该下标下没有元素,就是一个新的元素,没有发生hash冲突
            // 下一个next没有值,赋值为空
            node = new Node<>(key, value, null);
            // 实际存储的长度加1
            size ++ ;
        } else {
            // 已经发生了冲突
            Node<K, V> newNode = node;
            while (newNode != null) {
                // 这个判断是判断是否已经有了这个key,只是进行覆盖操作
                if (newNode.getKey().equals(key) || newNode.getKey() == key) {
                    return newNode.setValue(value);
                } else {
                    // 直接走到下一个节点,要进行添加值
                    if (newNode.next == null) {
                        // 已经遍历到了最后一个
                        // 下一个节点赋值为当前的node节点,表示,新加入的元素做第一个元素
                        node = new Node<>(key, value, node);
                        size ++ ;
                    }
                }
                newNode = newNode.next;
            }
        }
        // 将最新的节点放到table数组中的第一位
        table[index] = node;
        return null;
    }

    /**
     * 根据node查询key相等的node
     * @param node
     * @param k
     * @return
     */
    private 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;
    }


    /**
     * 根据key和数组的长度计算当前key应该存放在数组的哪个下标下面
     * @param key
     * @param length
     * @return
     */
    private int getIndex(K key, int length) {
        int hashCode = key.hashCode();
        return hashCode % length;
    }

    /**
     * 数组的扩容操作
     */
    private void resize(){
        // 1. 生成新的table是之前的2倍
        Node<K, V>[] newTable = new Node[DEFAULT_INITIAL_CAPACITY << 1];
        // 2. 需要重新计算index的索引,因为数组的长度变了,所以index需要重新计算
        // 遍历之前的数组,重新计算index
        for (int i = 0; i < table.length; i++) {
            Node<K, V> oldNode = table[i];
            while (oldNode != null) {
                // 为了尽快的进行垃圾回收,赋值为null
                table[i] = null;
                // 拿到key
                K oldKey = oldNode.getKey();
                // 重新计算index下标
                int index = getIndex(oldKey, newTable.length);
                // 得到当前节点的下一个节点
                Node<K, V> oldNext = oldNode.next;
                // 原来的node的下一个是最新的(原来的node oldNode该节点已经处理完毕)
                // 下面两部的思路就是把最新的节点,作为头节点
                oldNode.next = newTable[index];
                newTable[index] = oldNode;
                // 判断是否需要继续遍历循环
                oldNode = oldNext;
            }
        }
        // 3. 修改新的table数组
        table = newTable;
        DEFAULT_INITIAL_CAPACITY = newTable.length;
        // 赋值为null---为了垃圾回收机制能够回收
        newTable = null;
    }

    /**
     * 定义Entry的具体实现,自己实现的列表, 其实就是链表每一个节点的信息
     * @param <K>
     * @param <V>
     */
    class Node<K, V> implements Entry<K, V> {
        /**
         * 定义Map的key
         */
        private K key;

        /**
         * 定义Map的value
         */
        private V value;

        /**
         * 存放下一个节点的信息
         */
        private Node<K, V> next;

        /**
         * 构造函数
         * @param key
         * @param value
         * @param next
         */
        public Node(K key, V value, Node<K, V> next) {
            super();
            this.key = key;
            this.value = value;
            this.next = next;
        }

        @Override
        public K getKey(){
            return this.key;
        }

        @Override
        public V getValue(){
            return this.value;
        }

        @Override
        public V setValue(V value){
            // 用来保存赋值前的值
            V oldValue = value;
            this.value = value;
            return oldValue;
        }
    }
}

测试

public class Test {
    public static void main(String[] args) {
        ExtHashMap<String,String> extHashMap = new ExtHashMap<>();
        extHashMap.put("张三","22");
        System.out.println("--------张三===22--------"+extHashMap.get("张三"));
        extHashMap.put("张三","27");
        System.out.println("--------张三===27--------"+extHashMap.get("张三"));
        extHashMap.put("李四","35");
        System.out.println("--------李四===35--------"+extHashMap.get("李四"));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值