java HashSet源码简单剖析

1. 首先明确hash算法:
既然都是HashSet集合了,肯定与hash算法有关,我的理解就像是在查找新华字典(哈希表)一样,按照拼音(哈希值)先找到在哪页(哪个存储区域),再在该页(区域)查找。比全部遍历提高了查找效率。

2. HashSet集合是如何保证唯一性的?
通过追溯add()方法,了解到底层为HashMap的put(K key, V value)方法,源码如下:

 public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);  //与对象的hashCode()有关
        int i = indexFor(hash, table.length);//查找hash对应的存储区域位置
        //循环倒序比较(先进入的最后比较)该区域的值
        for (Entry<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;//添加失败
            }
        }

        modCount++;
        addEntry(hash, key, value, i);//按哈希值添加成功
        return null;
    }

很明显,元素唯一性和存储效率取决于对象的hashCode()和equals()方法。

3. 为什么要重写所装入对象的hashCode()和equals()方法?
如果不重写就会用继承自Object类的方法,而Object.hashCode()代表对象的十进制内存地址值,造成哈希值肯定不同,都不用equals()直接导致例如两个同名的学生对象能同时存在,无法满足“定制的”唯一性。
但是如果重写成如下方式了?

    public int hashCode() {
        return 0;
    }

把哈希值变成一样的(0),于是都能在一个区域内用equals()比较对象的成员变量是否相同,如果不同则添加到集合。看起来不错,但是就变成了遍历对象了,效率太低。于是我们需要定制规则让不同的对象哈希值不同,于是与对象的成员变量关联起来,但是简单的成员变量的哈希值相加的和可能会造成一样的哈希值,这就造成了冲突。为了尽可能区分,于是乘以一个数,如下系统生成的hashCode():(为什么是31?http://blog.csdn.net/tayanxunhua/article/details/20525251

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

这样,通过调用对象hashCode(),相同哈希值的就会放在一起,然后用重写的equals()判断是否成员变量一致;不同的就直接加到集合中。

更进一步,我们或许可以得出这样的结论(其实是规定):

  • 如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;
  • 如果两个对象的hashCode相同,它们并不一定相同。

当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值