zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    LeetCode 485. Max Consecutive Ones
    LeetCode 367. Valid Perfect Square
    LeetCode 375. Guess Number Higher or Lower II
    LeetCode 374. Guess Number Higher or Lower
    LeetCode Word Pattern II
    LeetCode Arranging Coins
    LeetCode 422. Valid Word Square
    Session 共享
    java NIO
    非阻塞IO
  • 原文地址:https://www.cnblogs.com/EmilZs/p/9309794.html
Copyright © 2011-2022 走看看