Tree Bins(即元素均为TreeNode的箱),主要由HashCode排序,但是在HashCode计算结果一致的情况下,如果两个元素是可比较的(即均继承Comparable接口,class C implements Comparable<C>),则用他们的比较方法(compareTo )进行排序(通过反射检查泛型类型以验证这一点,参见方法comparableClassFor)。当键具有不同的哈希值或可排序时,增加Tree bins的复杂度将会提供最坏情况的操作(O(logn))。因而,在HashCode()方法返回分布不均的值以及许多键都共享相同的HashCode()值(它们是可比较的),性能也会缓慢下降。如果这两种方法都不适用,那么与不采取预防措施相比,我们可能会在时间和空间上浪费大约两倍的时间。 但是唯一已知的情况是由于不良的用户编程已经导致程序运行非常缓慢,以至于几乎没有什么区别。
因为TreeNode的大小约为常规节点的两倍,所以我们仅在当bins拥有了足够多的节点时,才会使用它们。当bins拥有的节点变小时(通过remove或者调整大小),他们会转换成普通bins。在具有良好分布的hashCode的用法中,很少使用tree bins。尽管由于调整粒度,差异很大,理想情况下,在随机HashCodes下,bin中节点的频率遵循泊松分布(链接),其默认调整大小阈值为0.75,平均参数为0.5。忽略方差,列表大小k的预期出现次数是: $$ k = \frac{e^{-0.5}* 0.5^{k}}{k!} $$
1 2 3 4 5 6 7 8 9 10
* 0: 0.60653066 * 1: 0.30326533 * 2: 0.07581633 * 3: 0.01263606 * 4: 0.00157952 * 5: 0.00015795 * 6: 0.00001316 * 7: 0.00000094 * 8: 0.00000006 more: less than 1 in ten million
publicfinal V setValue(V newValue){ V oldValue = value; value = newValue; return oldValue; }
publicfinalbooleanequals(Object o){ if (o == this) returntrue; if (o instanceof Map.Entry) { Map.Entry<?,?> e = (Map.Entry<?,?>)o; if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue())) returntrue; } returnfalse; } }
staticfinalinttableSizeFor(int cap){ int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict){ Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // 如果table为空或者table的大小为0,则resize if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); // 如果hash计算的位置为null,则直接再tab[i]上面增加该节点 else { // 否则 Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; // 哈希值计算相等,value相等 elseif (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { // 增加值 for (int binCount = 0; ; ++binCount) { // p的next为空 if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); // 直接赋值 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); // 树化 break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; // key存在 p = e; // 赋值 } } if (e != null) { // existing mapping for key 如果已经存在 V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) // 超出阈值 resize resize(); afterNodeInsertion(evict); returnnull; }
2 putVal函数
1 2 3 4
public V get(Object key){ Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
publicclassThreadOneextendsThread{ Map<String, String> s = new HashMap<String, String>(); @Override publicvoidrun(){ for (int i = 0; i < 5; i++) { s.put(currentThread().getName() + i, "world"); for (Map.Entry e : s.entrySet()) { System.out.println(e.getKey() + "||" + e.getValue()); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
1 2 3 4 5 6 7 8 9
publicclassMyMain{ publicstaticvoidmain(String[] args){ ThreadOne threadOne = new ThreadOne(); Thread thread1 = new Thread(threadOne, "A"); Thread thread2 = new Thread(threadOne, "B"); thread1.start(); thread2.start(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
B0||world B0||world A1||world A1||world B0||world B1||world Exception in thread "A" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429) at java.util.HashMap$EntryIterator.next(HashMap.java:1463) at java.util.HashMap$EntryIterator.next(HashMap.java:1461) at ThreadOne.run(ThreadOne.java:32) at java.lang.Thread.run(Thread.java:745) A1||world B2||world B0||world B1||world
HashTable: Any non-null object can be used as a key or as a value. HashMap: This implementation provides all of the optional map operations, and permits null values and the null key.
【Q2】HashMap线程安全吗? 【A2】线程不安全
(The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.)