hash的的算法不用care,只要理解根据key得到hash值即可;再根据hash值,`indexFor(int h, int length)`计算桶(数组)的下标;这一步任务就是定位桶的下标。 for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } 这里可以发现for循环初始值HashMapEntry<K,V> e=table[i](i是上一步计算出的下标),HashMapEntry是一个链表结点,并拥有next结点;滑动链表结点,依次比较,当`e.hash == hash && ((k = e.key) == key || key.equals(k))`条件满足就找到了相同的key,进行修改操作,否则就进行增加操作,如下: addEntry(hash, key, value, i); 最后会执行到: void createEntry(int hash, K key, V value, int bucketIndex) { HashMapEntry<K,V> e = table[bucketIndex]; table[bucketIndex] = new HashMapEntry<>(hash, key, value, e); size++; } 由代码可以发现,新增的元素都是放在对应桶的链表的表头。由此可以得出不发生hash碰撞情况,插入操作的时间复杂度是O(1)的。 + remove函数 final Entry<K,V> removeEntryForKey(Object key) { if (size == 0) { return null; } int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key); int i = indexFor(hash, table.length); HashMapEntry<K,V> prev = table[i]; HashMapEntry<K,V> e = prev; while (e != null) { HashMapEntry<K,V> next = e.next; Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { modCount++; size--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } return e; } 可以发现主要的过程也是由key得到hash值,再定位桶的下标,继续遍历链表,寻找与之对应的key,之后都是链表操作就不再描述了。 + get函数 核心函数是: final Entry<K,V> getEntry(Object key) { if (size == 0) { return null; } int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key); for (HashMapEntry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; } (责任编辑:本港台直播) |