- HashMap
- HashMap的工作原理:
- HashMap是基于hash算法的原理,使用put(key, value)方法来存储对象,使用get(key)方法来获取对象。
- 1,当我们使用put()方法时,需要给它传递一个键值对,然后它调用键的hashCode()方法,返回的hashCode用于找到对应的bucket的位置(即table的下标)来储存Entry对象。
- 如果该key对应的键值对已经存在,则用新的value取代旧的value;返回了旧的value后,直接退出。
- 如果key为null,则将该键值对添加到table[0]中。
- 注:
- 1)当两个entry对象(一个是要新添加进来的对象,一个是已经存在的对象)的Key不同,而entry对象的hash值相同时,就会发生碰撞。HashMap使用链表存储对象,会把新添加的键值对存储在链表头部。
- 2)当HashMap中entry数组的实际大小(已放入entry的数量) 大于或等于阈值threshold(threshold = 容量*加载因子loadFactor)时,则
- 将会创建一个是原来entry数组(即table)大小两倍的entry数组,并将原来的entry对象放入新的entry数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置(即table的下标)
- 3)在多线程环境下使用HashMap可能导致死锁:
- 如果两个线程都发现HashMap需要重新调整大小了,它们可能会同时试着调整大小。如果条件竞争发生了,那么就死循环了。
- 在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素依次放在链表的尾部,而是依次放在头部,这是为了避免尾部遍历(tail traversing)。
- 故在多线程环境下应该使用ConcurrentHashMap或HashTable
- 2,当我们使用get()方法时,需要给它传递一个Key,然后它调用Key的hashCode()方法,返回的hashCode用于找到对应的bucket的位置(即table的下标),然后返回该位置上 hash值和key值均相等 的entry的value值。
- 如果传递的Key为null,则返回table[0]中key为null的entry的value值。如果没有找到,则返回null。
- 注:
- 1)当两个entry对象(一个是要新添加进来的对象,一个是已经存在的对象)的Key不同,而entry对象的hash值相同时,就会发生碰撞。此时,会调用key.equals()方法去找到链表中正确的entry对象的value值
- HashMap性能:
- 1)使用String、Interger等包装类适合作为键:
- String、Interger等包装类是final修饰的类,并且重写了equals()和hashCode()方法。
- 1,因为要计算键的hashCode,就必须保证键的值不会改变。如果一个键的值在放入时的hashcode和获取时的hashcode不同的话,那么就不能从HashMap中找到正确的entry对象。
- 2,键对象正确地重写equals()和hashCode()方法是非常重要的。hashCode()方法应尽量使两个不相等的对象返回不同的hashcode,那么碰撞的几率就会小些,这样HashMap的性能就能提高些。
- 说明:当每个bucket里存储的Entry只是单个Entry ———— 也就是没有通过指针产生Entry链时,此时的 HashMap 具有最好的性能
- 3,键的不可变性使得程序能够缓存不同键的hashcode,这将提高整个获取对象的速度,故HashMap的性能也可提高些。
- 4,String最常用
- 5,键的不可变性也相应地提高了线程安全。
- 2)加载因子的大小很重要
- 1,加载因子越大,对空间的利用越充分,但是查找的效率会降低(链表长度会越来越长);
- 2,加载因子太小,那么表中的数据将过于稀疏(很多空间还没用,就开始扩容了),对空间造成严重浪费。
- 3,系统默认加载因子为0.75,这是一个比较理想的值,一般情况下我们是无需修改的。
- 3)HashMap中则通过h&(length-1)的方法来代替取模,同样实现了均匀的散列,但效率要高很多。
- 哈希表的容量(entry数组的length)必须是2的整数次幂的原因:
- 1,length为2的整数次幂的话,h&(length-1)就相当于对length取模,这样便保证了散列的均匀,同时也提升了效率;
- 2,length为2的整数次幂的话,为偶数,这样length-1为奇数,奇数的最后一位是1,这样便保证了h&(length-1)的最后一位可能为0,也可能为1(这取决于hash值),即:与后的结果可能为偶数,也可能为奇数,这样便保证了散列的均匀性。
- 3,如果length为奇数的话,很明显length-1为偶数,它的最后一位是0,这样h&(length-1)的最后一位肯定为0,即只能为偶数,这样任何hash值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间。
原文链接:http://blog.csdn.net/wodewutai17quiet/article/details/46044143