to be man know hashmap most
hashmap里面的数学知识《链表到红黑树转化的阈值为啥是 8》
我看到了泊松分布
* Because TreeNodes are about twice the size of regular nodes, we
* use them only when bins contain enough nodes to warrant use
* (see TREEIFY_THRESHOLD). And when they become too small (due to
* removal or resizing) they are converted back to plain bins. In
* usages with well-distributed user hashCodes, tree bins are
* rarely used. Ideally, under random hashCodes, the frequency of
* nodes in bins follows a Poisson distribution
* (http://en.wikipedia.org/wiki/Poisson_distribution) with a
* parameter of about 0.5 on average for the default resizing
* threshold of 0.75, although with a large variance because of
* resizing granularity. Ignoring variance, the expected
* occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
* factorial(k)). The first values are:
1) 查找性能和存储之间的一个折中,有理有据,细节拉满。
2) 为啥是红黑树? 而不是平衡树
红黑树,在包含 插入,删除,查询操作的时候,性能要好于avl。
avl 是严格的二叉查找, 对于删除,修改会有很多的 rebalance炒作,当然查询更快。
rb-tree 是一个综合的性能折中。
红黑树的模型,需要另外的篇幅: 通过 好几个约束条件来保证, 黑色节点的平衡,
从而不完美的平衡,有比较不错的查询速度。
- 黑色根节点
- 红色节点子节点都是黑色
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
尽可能想要分散的hash函数
这个要从 后面的寻址说起,
hash &(length-1)
length 的长度通常不会很长,比如16位的话,那么hash 值,如果超过16位呢? 那么高位的数值就会丢失
就有可能造成,相对不均匀。例如:最极端的情况,就是一大堆 hash,就高位不一样,然后低位全部一样,
这个寻址后,就会造成分散不均匀。
hashmap在设计的时候,有很多这样的优化考虑。
所以,给定一个 key, 他并没有只用 key.hashCode.
还做了一定的操作
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
原理就是把hashcode与本身右移动16位之后,做一个与或,把高位16位的一些数值与低位16位搅合一下。
为啥size 都是2的 N次方。
1)利用到一个数学公司,当 len = 2^n次方的时候
hash% len = hash &(len-1).
要知道, 位运算的性能比 取莫要快很多。
2) 在hashmap resize 的时候, 保持这个特型的话,
可以让现有的元素非常快速的计算出 resize 之后的坐标,无需重新计算hash,也无需处理冲突。
一切都可以平移。
power-of-two masking,这个原理可以好好看看。
多线程相关 (1)【fail fast】
abstract class HashIterator {
Node<K,V> next; // next entry to return
Node<K,V> current; // current entry
int expectedModCount; // for fast-fail
int index; // current slot
HashIterator() {
expectedModCount = modCount;
*// 构造iterator 的时候,就设置了一个 snapshot下的 modCount *
Node<K,V>[] t = table;
current = next = null;
index = 0;
if (t != null && size > 0) { // advance to first entry
do {} while (index < t.length && (next = t[index++]) == null);
}
}
public final boolean hasNext() {
return next != null;
}
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
**调用 next 的时候都会检查 modCount是否已经修改,否则抛出异常**
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
**如果使用自己的remove方法,会更新这个值,就不会抛出 mdfe**
expectedModCount = modCount;
}
}
对于hashmap的foreach,以及key,value 等list 的foreach,都实现了fail-fast机制。
多线程相关(2) hashmap 为啥不是线性安全的?
https://www.cnblogs.com/developer_chan/p/10450908.html
HashMap是线程不安全的,其主要体现:
1.在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。
扩容的时候采用链表头插法
2.在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。
concurrentMap
1) 1.7 和1.8 的区别,各自的优劣
2) 为什么不用segment
3) cas的场景,synchronized 和 reenterlock 的却别和优劣
4) CounterCell @sun.misc.Contended 高级特型的作用
网友评论