Java HashMap源码分析与手写

1. HashMap是怎样一种数据结构

数组+ 链表 + 红黑树(jdk1.8引入,后面也是按照jdk1.8的来分析)

 

2. HashMap为什么取值快?
 

    HashMap在存储的时候,根据key的hash值(实际上还右移16再^hash值)&上数组的长度-1得到了在数组上的散列位置index,同样的方式,在取值的时候,和存储方式一致取得key值在数组上的散列位置index, 假如不存在散列碰撞的情况下,时间复杂度为O(1),效率很高
假如散列冲突比较多,将形成链表n个数据,则需要遍历链表,时间复杂度增大,查找的时间复杂度为O(n) (jdk1.8引入红黑树减小了因冲突查找效率低的问题,时间复杂度为log(n))。

   key的hash值处理代码:  

      static final int hash(Object key) {
         int h;
          return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
      }

   为什么不直接用hash值去使用?

   因为hash值比较大,比较耗内存。>>>即无符号右移,将得到高位的小的数字,^为异或,^的时候为右移的hash值高位都为0, ^的结果高位都会被处理掉。

 为什么用&来取代%来得到在数组的散列位置?

  因为&比%解释更快,hashmap中很多用到运算符代替数学运算符,如<<1 来代替乘以2,因为速度更快。

 

3. 为什么有些时候在知道要存储数量的时候,初始化HashMap的时候建议给容量大小初始化大小?

   这就涉及到HashMap的扩容了。在没有给定HashMap,当数组大小达到阀值threthreshold = 容量大小 * 加载因子(默认0.75)的时候,就需要进行扩容了,原来的数组长度乘以2扩容出新的数组,而扩容之后做的事情也比较耗费时间,也就是需要重新再做hash值散列到新的数组时间复杂度为O(n),假如只要放100个数据,不初始化容量的话,将扩容很多次,效率来说是很低的。其实我们可以在new Hash(100),的时候就传如初始化值,将减少扩容。当然也不能初始化容量太大,也比较浪费内存。传入100,那他的容量就初始化为100了?请看初始化容量大小代码。

static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

 

假如传如cap 那得到的值将是大于cap而接近2的n次方的,假如传入3,将得到4(2的2次方),传入10,的到16(2的4次方)

HashMap的大小为什么是2的n次方   

   为了散列key在数组上面,采用处理过的hash&length-1(length是数组的长度),得到在数组的散列位置。
   在&运算的时候,使得length-1 的二进制表示每一位都等于1,使得hash能够均匀的散列在数组上,这是一个有效减少散列碰撞的方法



4. jdk1.8之后,HashMap为为何引入红黑树? 

    在jdk1.7之前,key散列到数组上的发生冲突碰撞的解决方式就是使用链表来解决,用链表来解决会存在一些问题

    1. 链表的长度越长,查找的时间复杂度就越大,假如长度n,时间复杂度为O(n)。
    2. 多线程的情况下出现链表闭环(当然在多线程的情况下我们不建议用HashMap,可以使用ConcurrentHashMap)

    在链表长度大于8的时候,链表会被转换为红黑树,红黑树是一个带颜色的自平衡二叉搜索树,时间复杂度O(log(n)),长度小于6的时候转化为链表


5. 手写HashMap

/**
 * 文件描述:手写实现hashmap,不考虑边界情况,只用链表去解决冲突的实现
 * 作者:chenjingkun708
 * 创建时间:2020/3/20
 * 更改时间:2020/3/20
 */
public class MyHashMap<K,V>{

    private int threshold;//阀值
    private float loadFactor = 0.75f;//加载因子
    private Node<K,V>[] array;
    private String TAG = "android_test";
    private int size;
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认大小16
    public MyHashMap(){

    }

    public V put(K key,V value){
        //实际上可存在一个key为null
         if (key==null){
             return null;
         }
         //1.得到hash值的处理
         int h = hash(key);
//         Log.i(TAG,"处理过的hashcode="+h);
         //2. 得到数组长度-1
         Node<K,V>[] tab;
        Node<K,V> newNode;

         if ((tab=array)==null){
             tab = resize();
         }
        int index = h & tab.length-1;
         //考虑一,散列不冲突的情况
        Node<K,V> first = tab[index];
        if (first==null){
             newNode = new Node<>(key,value,h,null);
            tab[index] = newNode;
        }else {
            Node<K,V> cur = first;
            //查找链表有没有存在key相同的
            while (cur!=null){
                if (cur.hash==h&&(cur.key==key || cur.key.equals(key))){
                    //查找到key相同的,覆盖
                    cur.value = value;
                    return value;
                }
                if (cur.next==null){
                    break;
                }
                cur = cur.next;
            }
            //到了这里就是没有查找到了
            newNode = new Node<>(key,value,h,null);
            cur.next = newNode;
        }
        if (++size>threshold){
            resize();
        }
        return value;
    }

    //扩容
    private Node<K,V>[] resize() {
        int oldCap,newCap = 0;
        Node[] oldArray;
        oldCap = array==null?0:array.length;
        if (oldCap>0){
           newCap = array.length << 1;
        }else {
            newCap = DEFAULT_INITIAL_CAPACITY;
        }
        threshold = (int) (newCap*loadFactor);
        oldArray = array;
        array = new Node[newCap];
        //要散列老的到新的上面
        if (oldArray!=null&&oldArray.length>0){
            for (Node node:oldArray){
                if (node!=null){
                    while (node!=null){
                        int index = node.hash & newCap-1;
                        Node next = node.next;
                        node.next = null;
                        if (array[index]==null){
                            array[index] = node;
                        }else {
                            Node first = array[index];
                            while (first!=null){
                                if (first.next==null){
                                    first.next = node;
                                    break;
                                }
                                first = first.next;
                            }
                        }
                        node = next;
                    }

                }
            }

        }
        return array;
    }

    //处理hashcode
    private int hash(K key) {
        int h;
//        Log.i(TAG,"hashcode="+key.hashCode());
        return key==null?0:((h = key.hashCode())^ (h >>>16));
    }

    public int size(){
        return size;
    }

    public V get(K key){
        //实际上可以存在一个key为null的
        if (key==null||array==null){
            return null;
        }
        int h = hash(key);
        int index = h & array.length-1;
        Node<K,V> node = array[index];
        while (node!=null){
            if (node.hash==h&&(key==node.key||node.key.equals(key))){
                return node.value;
            }
            node = node.next;
        }
        return null;
    }

    //打印所有值
    public void printAll(){
        if (array!=null){
            for (Node<K,V> node : array){
                while (node!=null){
                    Log.i(TAG,"key="+node.key+",value="+node.value);
                    node = node.next;
                }
            }
        }else {
            Log.i(TAG,"数组为空");
        }
    }

    //数据节点
    static class Node<K,V>{
        private K key;
        private V value;
        private int hash;
        private Node<K,V> next;

        public Node(K key, V value, int hash, Node<K, V> next) {
            this.key = key;
            this.value = value;
            this.hash = hash;
            this.next = next;
        }
    }
}

 

测试:
 

MyHashMap myHashMap = new MyHashMap<>();
myHashMap.put('a',1);
myHashMap.put('b',2);
myHashMap.put("ab",3);
myHashMap.put("abcd",4);
myHashMap.put("adfg",5);
myHashMap.put("a1","a1");
myHashMap.put("a1","aa");
myHashMap.put("a2","a2");
myHashMap.put("a3","a3");
myHashMap.put("a4","a4");
myHashMap.put("a5","a5");
myHashMap.put("a6","a6");
myHashMap.put("a7","a7");
myHashMap.put("a8","a8");
myHashMap.put('a','a');

myHashMap.printAll();
int num = (int) myHashMap.get("adfg");
Log.i("android_test","取得数字:"+num);
Log.i("android_test","打印大小:"+myHashMap.size());

 

查看打印:

2020-03-20 18:10:38.397 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=a,value=a
2020-03-20 18:10:38.397 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=ab,value=3
2020-03-20 18:10:38.397 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=b,value=2
2020-03-20 18:10:38.397 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=adfg,value=5
2020-03-20 18:10:38.397 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=abcd,value=4
2020-03-20 18:10:38.397 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=a1,value=aa
2020-03-20 18:10:38.397 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=a2,value=a2
2020-03-20 18:10:38.397 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=a3,value=a3
2020-03-20 18:10:38.397 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=a4,value=a4
2020-03-20 18:10:38.397 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=a5,value=a5
2020-03-20 18:10:38.398 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=a6,value=a6
2020-03-20 18:10:38.398 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=a7,value=a7
2020-03-20 18:10:38.398 14791-14791/example.ndk.cjk.datastructdemo I/android_test: key=a8,value=a8
2020-03-20 18:10:38.398 14791-14791/example.ndk.cjk.datastructdemo I/android_test: 取得数字:5
2020-03-20 18:10:38.398 14791-14791/example.ndk.cjk.datastructdemo I/android_test: 打印大小:13

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值