文章导读目录
三、hashMap工作原理
详细介绍一下java集合容器及hashMap工作原理
一、Collection 、Map
java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。
ArrayList:底层数组,排列有序可重复,速度快,增删慢,线程不安全,容量不够时,当前容量1.5倍+1;
Vector:底层数组,排列有序可重复,速度快,增删慢,线程安全,容量不够时,默认扩展一倍容量;
LinkedList:底层双向循环链表结构,排列有序,可重复,线程不安全,查询慢,增删快;
HashSet:底层哈希表,无序不可重复,内部是HashMap,存取速度快;
TreeSet:底层二叉树,排列无序不重复,排序存储,内部是TreeMap的SortedSet。
LinkedHashSet:底层哈希表,并用双向链表记录插入顺序,内部是
LinkedHashMap;
Queue:在两端出入的list,也可以用数组或者链表实现;
HashMap:底层哈希表,键不可重复,值可以重复,访问速度快,线程不安全,key、value都可以空,对应的线程安全的有ConcurrentHashMap;
HashTable:底层哈希表,键不可重复,值可以重复,线程安全,key、value都不可以空,使用了synchronized进行控制;
TreeMap:底层二叉树,键不可重复,值可以重复;实现了SortMap接口,默认按键值升序排序;也可以指定排序比较器;使用TreeMap时key必须实现Comparable接口或在构造TreeMap传入自定义的Comparator;
LinkedHashMap:是HashMap的一个子类,保存了记录的插入顺序;用Iterator遍历时,先取到的肯定是先插入的;
小结:tree开头的底层是二叉树;hash开头的底层是哈希表;set的内部其实还是map,link开头的底层是双向循环链表结构。
二、常见的问题 1、HashTable、HashMap、ConcurrentHashMap的区别?
HashTable:
ConcurrentHashMap:支持多线程
HashMap:
三、hashMap工作原理1、hashMap基本组成(数组+链表+红黑树)
HashMap的主干是一个Entry数组,链表则是主要为了解决哈希冲突而存在的(链表长度超过8变成红黑树,低于6变回链表;);Entry是HashMap中的一个静态内部类。代码如下,包含4部分主要信息:
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;//存储指向下一个Entry的引用,单链表结构
int hash;//对key的hashcode值进行hash运算后得到的值,存储在Entry,避免重复计算
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
注:hashcode是32位的int类型!
2、HashMap工作原理
1)HashMap通过put和get方法存储和获取;
2)存储对象时将K/V传给put();调用hash()方法计算K的hash值,然后结合数组长度(n-1)& hash计算数组下标;
3)调整数组大小, 容器中元素个数大于capacity*loadfactor时,容器自动扩容resize为2n;
4)如果k的hash值在hashmap中不存在,则插入,若存在,则碰撞;
5)如果k的hash值在hashmap中存在,切equals()返回true,则更新键值对,若返回false,则插入链表的尾部(尾插法)或者红黑树中(树的添加方式),jdk1.7前是头插法;
6)获取对象时,将K传给get()hashset和hashmap区别,调用hash(K)找到该键值所在的数组下标, 若为链表,则在链表中通过key.equals(k)查找,O(n) ;若为树,则在树中通过key.equals(k)查找,O(logn);
HashMap数组扩容过程:
创建一个新数组,容量为旧数组的两倍,重新计算旧数组中结点的位置,结点在新数组中的位置只有两种:
①原下标位置;②原下标位置+旧数组大小;
3、Hash函数的设计
将key的hashCode与该hashCode的无符号右移16位,异或起来得到的。
因为当table的size比较小时,能影响到table下标的,只有哈希值几个低位bit,这很可能会加剧哈希碰撞。但这样实现后,哈希值的高16位bit保持不变,低16位则受到高16位的“扰动”而发生改变,这样就使得高位bit也能影响table下标,减少哈希碰撞。
异或是因为:
key的hashCode只要有一个bit发生变化,hash函数的返回值也会跟着变化,用以减少哈希碰撞。
4、为什么计算下标用h & (length-1)?
作用上相当于取模mod或者取余%。
这意味着数组下标相同,并不表示hashCode相同。位操作肯定比取余操作快多了。
5、为什么重写了hashCode方法,也应该重写equals方法?
自定义的类作为key,hashCode方法和equals方法要么都不重写hashset和hashmap区别,要么都重写。
如果key只重写了hashCode方法,却没有重写equals方法。那么会造成map里会存在重复的我们认为“相同”的键值对在里面(一般是指,两个对象的成员是相同的)。因为如果添加了相同元素,根据put过程则发生哈希碰撞,本来这个相同元素不应该新增,但由于原始Object的equals方法逻辑使用==判断,所以只要地址值不同就肯定能添加进去。
如果key只重写了equals方法,却没有重写hashCode方法。那么也会造成map里会存在重复的我们认为“相同”的键值对在里面。因为使用了原始的hashCode了,有些该发生的哈希碰撞也就不会发生了,都不在一个哈希桶了,即使我们认为是“相同”的,也不会去调用你重写的equals方法的。
限时特惠:本站每日持续更新海量设计资源,一年会员只需29.9元,全站资源免费下载
站长微信:ziyuanshu688