一、HashMap的数据结构?如何遍历最快
首先hashMap有一个静态内部类Node,并实现了Map.Entry接口;
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
Map.Entry接口中有获取key和value的方法
![]() |
2、HashMap的底层是一个静态数据
transient Node<K,V>[] table;
3、Node的结构是一个链表(hash冲突时使用)
![]() |
4、遍历速度问题
4.1使用keySet遍历
首要要在静态数据中将Key全部取出-->放到set中,如果需要再使用set中的key获取value时,需要再进行使用key从map中取值,map.get(k)使用的是hash检索模式
--相对来说不是很快
4.2使用 Set<Map.Entry<Integer, Integer>> entries = mapp.entrySet();
直接找到数组中的entry数据,放到set中,然后直接从entry中获取数据,
--速度比keySet块(不需要先获取key,然后再根据key来检索)
二、map中的哈希冲突是什么?如何解决
map存放数据使用的是put方法,
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
因为hashMap的底层是静态数组,在存放新值的时候首选要确定数组下边,通过上边代码可以看到首选是通过key的hash算法来确定下标,如果此时下标没有数据则直接放入,如果此时该下标内已经有数据了,则会将新数据以链表的形式接着往下放;当然如果链表的下标>=7(长度大于等于8)的时候,链表结构会变成红黑树;
hash冲突本质就是静态数组的索引下标冲突;不是hash值冲突(hash值可能是不冲突的)
索引下标计算方法如下:
p = tab[i = (n - 1) & hash]
三、HashMap、Hashtable、ConcurrentHashMap之间的区别
HashMap | 没有锁 |
Hashtable | 整表锁 |
ConcurrentHashMap | 分段锁 |
HashMap的方法都是不加锁的;
Hashtable的主要方法都使用了synchronized进行同步,他锁住了当前对象;简单说就是锁了整个hash表,在高并发的情况下效率非常低;
ConcurrentHashMap:分段锁
1.7及之前
使用的是Segment分段锁,继承了ReentrantLock类
使用的是HashEntry的静态内部类
1.8及以后
和hashMap就一致了使用的也是node内部类,
区别点在于比如在put方法中和hashMap的区别在于如果发生Hash冲突时,会锁住冲突下标的节点(也就是节点上的链表);大幅度简化了1.7的结构
synchronized锁的是当前对象?