• HashMap原理以及自己实现HashMap


    1、HashMap是什么?

      HashMap是java常用来存储键值对的数据结构,它是以key/value的形式存储的,它不是线程安全的,Key可以为null值。

    2、HashMap的实现原理

      HashMap的底层实现主要是基于数组和链表实现的,HashMap中以通过key值的HashCode值来计算Hash值的,通过Hash值来决定存储在数组的位置,也就是说将新元素插入到数组的这个位置来。当插入新元素时,如果新元素的Hash值与数组中这个位置上元素的Hash值相等,就会出现Hash冲突,那么就会将新元素的next指向数组中的这个位置的元素,新元素就插入到这个位置来。

      结构图:

    3、自己编写HashMap类

     Map接口
    1
    public interface Map{               2 int size;                   3 boolean isEmpty();             4 Object get(Object key); 5 Object put(Object key,Object value); 6 interface Node{ 7 Object getKey(); 8 Object getValue(); 9 } 10 }

    实现HashMap类

    public class HashMap implements Map{
        publci final int DEFAULT_CPACITY=16;      //数组默认容量
        private int size=0;
        Node[] node =new Node[DEFAULT_CPACITY];
        public int size(){
            return size;
        }
    
    
        public boolean isEmpty(){
            return size==0;
        }
    
    
        public Object get(Object key){
            int keyHashCode=hash(key);
            int keyIndex=indexOf(keyHashCode,node.length);
            for(Nodes eNode=node[keyIndex];eNode!=null;eNode=eNode.next){
                if(eNode.hashValue==keyHashCode&&eNode.key==key)
                    return eNode.value;
            }
            return null;
        }
    
    
    
        public Object put(Object key,Object value){
            int keyHashCode=hash(key);                //获得key的Hash值
            int keyIndex=indexOf(keyHashCode,node.length);    //通过Hash值获得key在数组中的位置
            for(Nodes eNode=node[keyIndex];eNode!=null;eNode=eNode.next){        //遍历key位置下的所有node结点,如果找到了新value替换旧value,并返回旧value
                if(eNode.hash==keyHashCode&&(eNode.key==key||eNode.key.equals(key))){
                    Object oldObj=eNode.value;
                    eNode.value=eNode.value;
                    return oldObj;
                }
            }
            addEntry(key,value,keyHashCode,keyIndex);       //如果没有找到与key对应的结点,就在链表头新增此结点
            return null;
        }
    
       /*在链表头新增结点*/
       /*如果超过了原始数组大小,则扩大数组*/
        public void addEntry(Object key,Object value,int keyHashCode,int keyIndex){
            if(++size==node.length){
                resize(2*node.length) 
            }
            Node nodeNext=node[keyIndex];
            node[keyIndex]=new Node(keyHashCode,key,value,nodeNext);
        }
        void resize(int newCapacity) {
            Node[] oldNode = node;
            int oldCapacity = oldNode.length;
    
            Node[] newNode = new Node[newCapacity];
            transfer(newNode, initHashSeedAsNeeded(newCapacity));
            node= newNode;
            threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
        }
        void transfer(Node[] newTable, boolean rehash) {
            int newCapacity = newTable.length;
         //for循环中的代码,逐个遍历链表,重新计算索引位置,将老数组数据复制到新数组中去(数组不存储实际数据,所以仅仅是拷贝引用而已)
            for (Node e : node) {
                while(null != e) {
                    Node next = e.next;
                    if (rehash) {
                        e.hash = null == e.key ? 0 : hash(e.key);
                    }
                    int i = indexFor(e.hash, newCapacity);
              //将当前entry的next链指向新的索引位置,newTable[i]有可能为空,有可能也是个entry链,如果是entry链,直接在链表头部插入。
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                }
            }
        }
    
    
        public int indexOf(int keyHashCode,int length){
            return keyHashCode%length;
        }
    
        
        public int hash(Object key){
            return key.hashCode();
        }
        
        static class Nodes impletents Map.Node{
            Object key;
            Object value;
            int hash;
            Nodes next;
            public Nodes(int hash,Object key,Objectvalue,Nodes next){
                this.hash=hash;
                this.key=key;
                this.value=value;
                this.next=next;
            }
            public Object getKey(){
                return this.key;
            }
            public Object getValue(){
                return this.value;
            }
        }            

     总结:

      实现原理就是数组和链表的结合,数组中存放的是这一组HashCode值最新的一个结点,通过这个结点,以链表的形式存放不同的key值的结点。

      在这里简述一下实现步骤:

        1:先写好Map接口,表明HashMap中要做的事,声明结点node接口;

        2:编写HashMap类实现Map接口;

        3:声明结点数组,并实现get和put方法(这里只实现了put和get方法),首先根据Key的值获得key对应的HashCode值,再根据HashCode值找到在数组中的下标,这个位置就是保存所有HashCode值为这个值的首结点,也就是链表头。

        4:实现get方法,通过链表头一个个遍历打到对应的key,并返回对应value,如果没有对应key,则返回空。

        5:实现put方法,也是先遍历链表,如果存在对应的key,则新值替换旧值并返回旧值。如果不存在,则添加新值到链表头(单向链表添加节点最快的方式),在添加节点之前如果size达到了临界值,就需要对数组进行扩容(新建一个数组,并将所有数据拷贝到新数组,如果数组进行扩容,数组长度发生变化,而存储位置 index = hashCode%node.length,index也可能会发生变化,需要重新计算index)。

        其它属性和方法也都是通过这种获得链表头,遍历链表的形式进行的。

    如果想要看更加详细的HashMap的原理,可参照博客:http://www.cnblogs.com/chengxiao/p/6059914.html#t3

  • 相关阅读:
    关于aar 上传到jcenter的最快方式
    快速开发的几个框架
    git 删除本地提交记录
    git 缓存溢出
    vs 启动网站设置为127.0.0.1 设置为本机IP地址
    uni-app 设置登录状态保存
    c# 快速实现php的ksort函数
    宝塔面板出现“require(): open_basedir restriction in effect. ”的解决方法
    PHP访问数据的时候 返回的json数据前面会带小红点
    C# Generic(转载)
  • 原文地址:https://www.cnblogs.com/EmilZs/p/9309794.html
Copyright © 2020-2023  润新知