博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HashMap的代码
阅读量:5826 次
发布时间:2019-06-18

本文共 5420 字,大约阅读时间需要 18 分钟。

hot3.png

2016-08-19 16:00

为什么两个对象equals返回true,可以hashcode不相等?

两个对象通过equals比较返回true.但是hashcode!=是一种什么样的情况.是为了节省HashMap的底层数组空间.也没有必要就完全不冲突.有一丢丢冲突的话.不用没一个Entry都用一个buckt啊.能省一些.

总之如果两个对象通过equals比较相等并且hashcode也相等的时候. 新添加的kv对v会覆盖掉原来的老的v.并且添加完成以后put方法返回老的被替换的v.

HashMap底层存储结构就是一个类型为HashMapEntry的数组

HashMapEntry就是一个单链表.存储了自己的key,value,hashcode以及下个HashMapEntry.

static class HashMapEntry
implements Entry
{ final K key; V value; final int hash; HashMapEntry
next; HashMapEntry(K key, V value, int hash, HashMapEntry
next) { this.key = key; this.value = value; this.hash = hash; this.next = next; } @Override public final boolean equals(Object o) { if (!(o instanceof Entry)) { return false; } Entry
e = (Entry
) o; return Objects.equal(e.getKey(), key) && Objects.equal(e.getValue(), value); } @Override public final int hashCode() { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); }}

对于put(k,v)方法根据k的值是否为null被当成两种情况来对待.

当key为null的情况

private V putValueForNullKey(V value) {    HashMapEntry
entry = entryForNullKey; if (entry == null) { addNewEntryForNullKey(value); size++; modCount++; return null; } else { preModify(entry); V oldValue = entry.value; entry.value = value; return oldValue; }}
@Override public V put(K key, V value) {        if (key == null) {            return putValueForNullKey(value);        }        int hash = Collections.secondaryHash(key);        HashMapEntry
[] tab = table; int index = hash & (tab.length - 1); for (HashMapEntry
e = tab[index]; e != null; e = e.next) { if (e.hash == hash && key.equals(e.key)) { preModify(e); V oldValue = e.value; e.value = value; return oldValue; } } // No entry for (non-null) key is present; create one modCount++; if (size++ > threshold) { tab = doubleCapacity(); index = hash & (tab.length - 1); } addNewEntry(key, value, hash, index); return null; }
int hash = Collections.secondaryHash(key);int index = hash & (tab.length - 1);public static int secondaryHash(Object key) {        return secondaryHash(key.hashCode());}private static int secondaryHash(int h) {        // Spread bits to regularize both segment and index locations,        // using variant of single-word Wang/Jenkins hash.        h += (h <<  15) ^ 0xffffcd7d;        h ^= (h >>> 10);        h += (h <<   3);        h ^= (h >>>  6);        h += (h <<   2) + (h << 14);        return h ^ (h >>> 16);    }

对于任意一个非null的key,总是能通过计算,获得一个整型值.这个整型值就是存储的bucket应该在HashMapEntry[]数组中的index. 这个是为什么? 计算出来的index就不会大于table数组的长度吗?我艹,太他妈难看懂了

public HashMap() {        table = (HashMapEntry
[]) EMPTY_TABLE; threshold = -1; // Forces first put invocation to replace EMPTY_TABLE}
/**     * Min capacity (other than zero) for a HashMap.     * Must be a power of two greater than 1 (and less than 1 << 30).     */    private static final int MINIMUM_CAPACITY = 4;         /**     * An empty table shared by all zero-capacity maps (typically from default     * constructor). It is never written to, and replaced on first put. Its size     * is set to half the minimum, so that the first resize will create a     * minimum-sized table.     */    private static final Entry[] EMPTY_TABLE            = new HashMapEntry[MINIMUM_CAPACITY >>> 1];
/**     * The table is rehashed when its size exceeds this threshold.     * The value of this field is generally .75 * capacity, except when     * the capacity is zero, as described in the EMPTY_TABLE declaration     * above.     */         private transient int threshold;

也即HashMap默认构造方法创建的是一个长度为2的数组.日了名字还得叫EMPTY_TABLE. 但是他没有用.而是会在第一次put进元素的时候resize. 当第一次put进元素的时候直接进入代码.

@Override public V put(K key, V value) {        if (key == null) {            return putValueForNullKey(value);        }            //A        int hash = Collections.secondaryHash(key);        HashMapEntry
[] tab = table; int index = hash & (tab.length - 1); for (HashMapEntry
e = tab[index]; e != null; e = e.next) { if (e.hash == hash && key.equals(e.key)) { preModify(e); V oldValue = e.value; e.value = value; return oldValue; } } //A // No entry for (non-null) key is present; create one modCount++; //B if (size++ > threshold) { tab = doubleCapacity(); //CC index = hash & (tab.length - 1); } //B addNewEntry(key, value, hash, index); return null; }

因为是第一次put元素.所以必然不会经过A段代码. 并且threshold的值默认为0.那么就会进入B段代码. 并且CC处index的值又变了.变成 index = hash & (tab.length -1);

tab = doubleCapacity();

这一行代码并不是简单的扩容两倍.还会对原先的老数据搞什么鸡巴rehash.看不懂了这就

void addNewEntry(K key, V value, int hash, int index) {        table[index] = new HashMapEntry
(key, value, hash, table[index]);}

转载于:https://my.oschina.net/tanghaoo/blog/734656

你可能感兴趣的文章
C++ primer plus
查看>>
python mysqlDB
查看>>
UVALive 3942 Remember the Word Tire+DP
查看>>
从微软的DBML文件中我们能学到什么(它告诉了我们什么是微软的重中之重)~目录...
查看>>
被需求搞的一塌糊涂,怎么办?
查看>>
c_数据结构_队的实现
查看>>
jquery 选择器总结
查看>>
Qt设置背景图片
查看>>
【阿里云文档】常用文档整理
查看>>
java中的Volatile关键字
查看>>
前端自定义图标
查看>>
Vagrant的一个BUG - 不支持'change_host_name'
查看>>
实验二
查看>>
独立开发一个云(PaaS)的核心要素, Go, Go, Go!!!
查看>>
MyBatis使用DEMO及cache的使用心得
查看>>
网站文章如何能自动判定是抄袭?一种算法和实践架构剖析
查看>>
【OpenCV学习】滚动条
查看>>
ofo用科技引领行业进入4.0时代 用户粘性连续8个月远甩摩拜
查看>>
兰州青年志愿者“中西合璧”玩快闪 温暖旅客回家路
查看>>
计划10年建10万廉价屋 新西兰政府:比想象中难
查看>>