zoukankan      html  css  js  c++  java
  • 手写HashMap

    经过HashMap源码分析,我们可以手写一个简单的HashMap

    废话不多说,直接上代码

    一个简单的HashMap拥有的方法,我把它定义为一个接口

    public interface MyMap<K, V> {
    
    	public V put(K k, V v); //插入
    
    	public V get(K k);//获取元素
    
    	public int size();//元素大小
    
    	interface Entry<K, V> {//键值对
    
    		K getKey();
    
    		V getValue();
    
    		V setValue(V value);
    	}
    }
    

      HashMap手写代码

      

    public class MyHashMap<K, V> implements MyMap<K, V> {
        // 1.定义table 存放HasMap 数组元素 默认是没有初始化容器 懒加载
        Node<K, V>[] table = null;
        // 2. 实际用到table 存储容量 大小
        int size;
        // 3.HashMap默认负载因子,负载因子越小,hash冲突机率越低, 根据每个链表的个数
        float DEFAULT_LOAD_FACTOR = 0.75f;
        // 4.table默认初始大小
        static int DEFAULT_INITIAL_CAPACITY = 16;
    
        @SuppressWarnings("unchecked")
        @Override
        public V put(K key, V value) {
            // 第一次初始化
            if (table == null) {
                table = new Node[DEFAULT_INITIAL_CAPACITY];
            }
    
            // 扩容 如果数组大小大于 负载因子乘以默认大小 就扩容
            if (size > DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR) {
                resize();
            }
            // 根据key获取 新元素在数组中的下标
            int index = getIndex(key, DEFAULT_INITIAL_CAPACITY);
    
            // 获取当前节点
            Node<K, V> node = table[index];
            if (node == null) {
                // node为空 代表当前位置的值为空 直接插入。
                node = new Node<>(key, value, null);
                size++;
            } else {// 代表当前元素有值
                // 保存原链表
                Node<K, V> newNode = node;
                while (newNode != null) {
                    // 如果发生key冲突,则值直接覆盖 并返回原node
                    if (newNode.key.equals(key) || newNode.key == key) {
                        return newNode.setValue(value);
                    } else {
                        // 如果未发送key冲突,代表新元素。 插入到链表的头位置。
                        if (newNode.next == null) {
                            node = new Node<>(key, value, node);
                            size++;
                        }
                    }
                    newNode = newNode.next;
                }
            }
            // 赋值
            table[index] = node;
            return value;
        }
    
        // 根据Length获取 key在数组的下标
        private int getIndex(K key, int length) {
            int hashCode = key.hashCode();
            int index = hashCode % length;
            return index;
        }
    
        /**
         * threshold(capacity * loadFactor) 扩容扩容之后的是倒序的链表
         */
        @SuppressWarnings("unchecked")
        private void resize() {
            // 新数组的长度为原数组的二倍
            Node<K, V>[] newTable = new Node[DEFAULT_INITIAL_CAPACITY << 1];
            // 遍历原数组 把原数组的每个元素放入新数组中
            for (int i = 0; i < table.length; i++) {
                Node<K, V> oldNode = table[i];
                table[i] = null;// 赋值为null---为了垃圾回收机制能够回收 将之前的node删除
                while (oldNode != null) {
                    // 获取该元素的在新数组的下标
                    int index = getIndex(oldNode.key, newTable.length);
                    // 保留原Node的下一个
                    Node<K, V> oldNext = oldNode.next;
                    // 为新数组赋值
                    oldNode.next = newTable[index];
                    newTable[index] = oldNode;
                    // 继续遍历
                    oldNode = oldNext;
                }
            }
    
            table = newTable;
            DEFAULT_INITIAL_CAPACITY = newTable.length;
            newTable = null;/// 赋值为null---为了垃圾回收机制能够回收
        }
    
        // 根据键获取值
        @Override
        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.key.equals(k)) {
                    return node;
                }
                node = node.next;
            }
            return null;
        }
    
        @Override
        public int size() {
            return size;
        }
    
        static class Node<K, V> implements Entry<K, V> {
    
            final K key;
    
            V value;
    
            Node<K, V> next;
    
            Node(K key, V value, Node<K, V> next) {
                this.key = key;
                this.value = value;
                this.next = next;
            }
    
            @Override
            public K getKey() {
                return key;
            }
    
            @Override
            public V getValue() {
                return value;
            }
    
            @Override
            public V setValue(V value) {
                V oldValue = value;
                this.value = value;
                return oldValue;
            }
    
        }
    
        // 打印
        public void print() {
            for (int i = 0; i < table.length; i++) {
                Node<K, V> node = table[i];
                System.out.print("下标位置[" + i + "]");
                while (node != null) {
                    System.out.print("[ key:" + node.getKey() + ",value:" + node.getValue() + "]");
                    node = node.next;
                }
                System.out.println();
            }
        }
    
    }
  • 相关阅读:
    删除 Visual studio 生成后的临时文件
    C# 中的委托和事件(转)
    C#复制DataRow出现“该行已经属于此表”错误的解决办法(转)
    ini配置文件读取类
    c# wpf窗体前端显示问题
    注册系统热键类(原创)
    C# 窗体最小化的托盘/系统通知区域(转)
    php explode()返回值
    $_SERVER['SCRIPT_NAME']
    svn合并初次使用心得
  • 原文地址:https://www.cnblogs.com/laolei11/p/11534501.html
Copyright © 2011-2022 走看看