有没有有顺序的map实现类?有哪几个?是如何保证它的顺序的?HashMap的随机存取和快速查找是如何实现的?
1.HashMap不是有序的;
2.TreeMap和LinkedHashMap是有序的(TreeMap默认升序,LinkedHashMap则记录了插入顺序)。
3.TreeMap的实现:
4.LinkedHashMap的实现
点击获取大神作品
水平太菜导致精力有限,有序map留着以后看,目前着重研究了最经常用到的HashMap。
5.HashMap的实现:
1)HashMap的数据结构
数组寻址容易,插入删除困难。链表插入删除容易,寻址困难。不知道哪位牛人一开始提出了哈希表的概念,结合了数组和链表的优点。
哈希表有多种不同的实现方法,最常用的一种方法叫拉链法,我们可以理解为“链表的数组” ,如图:
一个长度为16的数组,存储的是每一条链表的头结点。那么此时一个很重要的问题需要解决,关键字和地址怎么进行映射。Hash表采用一个映射函数 f : key —> address 将关键字映射到该记录在表中的存储位置,这种映射关系称作为Hash函数。Hash函数的设计有很多方法,构造Hash函数时应尽量考虑关键字的分布特点来设计函数使得Hash地址随机均匀地分布在整个地址空间当中。我介绍一种比较好理解的:除留取余法
如果知道Hash表的最大长度为m,可以取不大于m的最大质数p,然后对关键字进行取余运算,address(key)=key%p。
在这里p的选取非常关键,p选择的好的话,能够最大程度地减少冲突,p一般取不大于m的最大质数。一般而言,p取值Hash表的长度m。例如上图中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。
HashMap的源码,其中参数initialCapacity就代表了该数组的长度:
public HashMap(int initialCapacity, float loadFactor) {
//初始容量不能<0
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: "
+ initialCapacity);
//初始容量不能 > 最大容量值,HashMap的最大容量值为2^30
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//负载因子不能 < 0
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: "
+ loadFactor);
// 计算出大于 initialCapacity 的最小的 2 的 n 次方值。
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
//设置HashMap的容量极限,当HashMap的容量达到该极限时就会进行扩容操作
threshold = (int) (capacity * loadFactor);
//初始化table数组
table = new Entry[capacity];
init();
}
- 从源码中可以看出,每次新建一个HashMap时,都会初始化一个table数组。table数组的元素为Entry节点。
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
.......
}
- Entry是HashMap里面实现一个静态内部类,其重要的属性有 key , value,
next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。
2)存取实现
点击获取大神作品
存取部分,本来已经码了好多,感觉写的不清晰,还是附上大神的作品吧。
总之HashMap的源码设计的非常精彩,凝结了先贤们的智慧,读的非常过瘾。
集合相关问题:
1)HashMap、LinkedHashMap、ConcurrentHashMap、ArrayList、LinkedList的底层实现。
哈希表、哈希表&双向链表、多个子哈希表? 、数组、链表
2)HashMap和Hashtable的区别。
3)HashMap和ConcurrentHashMap的区别。
ConcurrentHashMap的设计者Doug Lea,(来自百度百科)
说他是这个世界上对Java影响力最大的个人,一点也不为过。因为两次Java历史上的大变革,他都间接或直接的扮演了举足轻重的角色。2004年所推出的Tiger。Tiger广纳了15项JSRs(JavaSpecificationRequests)的语法及标准,其中一项便是JSR-166。JSR-166是来自于Doug编写的util.concurrent包。
4)HashMap和LinkedHashMap的区别。
对于LinkedHashMap而言,它继承与HashMap、底层使用哈希表与双向链表来保存所有元素。其基本操作与父类HashMap相似,它通过重写父类相关的方法,来实现自己的链接列表特性。
LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。
注意,此实现不是同步的。如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。
5)HashMap是线程安全的吗?
不是,参考第3题。
6)ConcurrentHashMap是怎么实现线程安全的。
参考第3题。