关于HashSet 传入对象,怎么过滤
HashSet底层的数据结构用的是HashMap,使用Map的key存值,Value存放一个固定的Object,这是适配器模式。
如果传入的是两个对象,我们通过怎样的机制来判断他们是不是相等的,旧的会不会替换新的对象,以及怎么替换或者不替换呐?
是不是相等的?
//若存在相同,则直接覆盖value,返回旧value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value; //旧值 = 新值
e.value = value;
e.recordAccess(this);
return oldValue; //返回旧值
}
这是代码的判断语句,可以看出,这里用hash和equals都比较了,需要俩个都返回true才可以
需要说明的是hash
并不等同hashCode
方法,而是根据hashCode
计算出来的,二者结果线性相关.
//求hash值的方法,重新计算hash值
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
如果我们没有实现hashCode
或者equals
,二者都会调用Object
上的方法,Object
上hashCode
方法是一个本地方法,返回的值应该是对象在内存中地址,而equals
方法内是通过==
判断地址是否一致.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
所以,这一问的结论就有了,hashCode
和equals
方法都会用来判断相等,根据hashCode
计算出的hash相等(这和直接要求hashCode
相等有些区别),而且equals
返回true
时,才断定二者相等.
旧的会不会替换新的对象?
Value
我们知道,旧的会被新传入的Value
替换,那么Key
呢?
答案是不会替换,依旧是旧值,其实这个结论试一下就出来了,所以接下来,我们说一下细节,也就是如何判断不替换?
如何判断不替换?
之前我们提到Set
里套了Map
,Set
的操作是适配到Map
上完成的,所以Set.add
方法也是这样,需要注意的是add
方法有返回值,如果传入的Set
中存在的重复值返回fasle
表示add
失败,传入不存在的新值返回true
表示add
成功.
- 1
- 2
- 3
- 1
- 2
- 3
这里判断map.put
的操作是否为null来决定返回true
还是false
,而map.put
什么情况下返回null
,不返回null
返回什么呢?
HashMap.put实现细节
// 将“key-value”添加到HashMap中
public V put(K key, V value) {
// 若“key为null”,则将该键值对添加到table[0]中。
if (key == null)
return putForNullKey(value);
// 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
// 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
// 若“该key”对应的键值对不存在,则将“key-value”添加到table中
modCount++;
//将key-value添加到table[i]处
addEntry(hash, key, value, i);
return null;
}
这个方法返回一个V,也就是Map 里放入Value,查看代码,可以知道:
1. 如果put
入Map
的是一个已存在的Key
,那么在将Key
对应的Value
更新后,将返回旧的Value
,也就是oldValue
2. 如果put
入Map
的是新的Key
,那么将返回null
这样,就可以串起来了:
1. HashMap.put
传入新的Key-Value
,返回null
,HashSet
判断返回结果是null
后,add
方法返回true
表示增加成功
2. HashMap.put
传入已存在的Key
和新的Value
,返回oldValue
,HashSet
判断返回结果不是null
后,add
方法返回false
表示增加失败