HashMap介绍

        HashMap继承了AbstractMap类实现了Map、Cloneable、Serializable接口。继承关系上看是从Java中顶级从父类Object开始到抽象类的AbstractMap再到的子类HashMap。

   Hash实际上就是散列,当我们输入一个值的时候首先会进行一次散列算法,将长度不同的输入转换成固定长度的输出。每一个Object对象都有一个hashCode值,经过hash计算后的hash值在物理空间上对空间进行了压缩,也说明了应当存储在哪一个bucket上面。不同的Key值有可能会计算出相同的hash值,也就带来常见的hash碰撞的问题,对于碰撞问题一般采取的措施是通过链地址法和开地址发进行解决。java中提供的hashCode方法对于两个不同的对象在返回值不超出int类型2^32所计算的hashcode是不会重复的。但是空间跨度上对于内存而言还是很庞大的,所以需要对Hash的低位和高位使用异或操作进行整合。在计算位置的indexFor(int h, int length)中hash与数组长度-1会进行与计算,所以默认数组长度为16并且要求它必须为2的幂数,当对它进行-1操作后必然会保留地位全为1的二进制数,与操作结束后会将高位清0而保留低位。     

        在java1.7中hash()方法是:h ^= (h >>> 20) ^ (h >>> 12);  return h ^ (h >>> 7) ^ (h >>> 4) 

        在java1.8中hash()方法是:int h return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

        实际上对hash()方法而言是做了简化,1.7中任何一位的变化都会对返回值造成影响,1.8中则是把高16位的变化反映到低16位中。

          主要的参数:

          static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //初始容量是16且必须是2的幂数
          static final int MAXIMUM_CAPACITY = 1 << 30;//最大容量

          static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认负载因子0.75

        默认属性的HashMap最多存放:16*0.75=12个元素超过后,会调用rehash()方法增大表的容量。新容量将是原来的2倍,每个元素也需要重新计算要存放的位置,存到新数组中。在这个过程中代价是很大的,再多线程的情况下可能导致条件竞争问题,发生死锁,所以hashMap是线程不安全的。在Java1.8中引入了红黑树对链表作了补充,对这个问题可能会有改善。

        关于构造方法:

        public HashMap(int initialCapacity, float loadFactor)、public HashMap(int initialCapacity)、public HashMap(Map<? extends K, ? extends V> m)、public HashMap()。都取决于HashMap(int initialCapacity, float loadFactor)。

        

  1. public HashMap(int initialCapacity, float loadFactor) {
  2. //初始容量不能<0
  3. if (initialCapacity < 0)
  4. throw new IllegalArgumentException( "Illegal initial capacity: "
  5. + initialCapacity);
  6. //初始容量不能 > 最大容量值,HashMap的最大容量值为2^30
  7. if (initialCapacity > MAXIMUM_CAPACITY)
  8. initialCapacity = MAXIMUM_CAPACITY;
  9. //负载因子不能 < 0
  10. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  11. throw new IllegalArgumentException( "Illegal load factor: "
  12. + loadFactor);
  13. // 计算出大于 initialCapacity 的最小的 2 的 n 次方值。
  14. int capacity = 1;
  15. while (capacity < initialCapacity)
  16. capacity <<= 1;
  17. this.loadFactor = loadFactor;
  18. //设置HashMap的容量极限,当HashMap的容量达到该极限时就会进行扩容操作
  19. threshold = ( int) (capacity * loadFactor);
  20. //初始化table数组
  21. table = new Entry[capacity];
  22. init();
  23. }
        主要是对于临界值的把控,并且在每次新建HashMap时,都初始化一个table数组,数组的元素都为Entry节点。

    Put方法:

  1. public V put(K key, V value) {
  2. //null调用putForNullKey方法
  3. if (key == null)
  4. return putForNullKey(value);
  5. //hash值
  6. int hash = hash(key.hashCode());
  7. int i = indexFor(hash, table.length) ;
  8. //找到 key 保存的位置
  9. for (Entry<K, V> e = table[i]; e != null; e = e.next) {
  10. Object k;
  11. //若hash相同,覆盖value,返回旧value
  12. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
  13. V oldValue = e.value;
  14. e.value = value;
  15. e.recordAccess( this);
  16. return oldValue;
  17. }
  18. }
  19. modCount++;
  20. //将key、value添加至i位置处
  21. addEntry(hash, key, value, i);
  22. return null;

        e.hash == hash && ((k = e.key) == key || key.equals(k))条件是为了保证hash值相同的情况下找到正确的K-V。

       get方法会根据key取到value。在HashMap在存储过程中key-value是作为一个整体来存储的,即Entry对象。key的hashcode决定了Entry在数组中的存储位置,在取时也要根据key的hashcode取出相对应的Entry对象,而不是直接取出Value。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值