太有意思了
时间: 2020.11.30
问题分析:
记一次哈希冲突, 又是一个小知识。 >> 其实是错误, 这并不是哈希冲突。
一开始我认为这是 set 集合,默认是 hashset 了, hashset的存储在底层就是使用 hashmap 的 桶数组存储的。
通过 hashmap 唯一 key 值的特性来达到去重效果.
而对于自定义对象, 我们一般需要同时重写 hashcode 和 equals 方法,至于为什么要同时重写, 可以百度 为什么重写 equals 时必须重写 hashCode 方法?
自然而然, 只要我重写了 s N o d e sNode sNode 对象的这两个方法,这个问题应该就可以解决了.
结果非常的 amazing 啊 , 没有任何作用.
为了故事的顺利发展:
准备测试的核心代码, 已经重写了两个方法
public static void main(String[] args) {
sNode a = new sNode(1, 'l');
sNode b = new sNode(5, 'o');
sNode c = new sNode(4,'a');
Set<sNode> s = new TreeSet<>(new Comparator<sNode>() {
@Override
public int compare(sNode o1, sNode o2) {
return o1.count - o2.count;
}
});
s.add(a);
s.add(b);
System.out.println(s.contains(c));
s.add(c);
System.out.println(Arrays.toString(s.toArray(new sNode[0])));
}
class sNode{
int count;
char c;
public sNode(int count, char c){
this.count = count;
this.c = c;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
sNode sNode = (sNode) o;
return count == sNode.count &&
c == sNode.c;
}
@Override
public int hashCode() {
return Objects.hash(count, c);
}
}
这是必然没有冲突的,都存进去了.
上面代码修改
sNode a = new sNode(1, 'l');
sNode b = new sNode(1, 'o');
sNode c = new sNode(1,'a');
结果:
这两个方法并没有触发。
跟着代码 debug >> System.out.println(s.contains(c));
进入到 contains 方法
// 第一步
public boolean contains(Object o) {
return m.containsKey(o);
}
// 第二步
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
// 第三步
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null) // 拥有自定义的比较器
return getEntryUsingComparator(key); // 将会进入这里
if (key == null)
throw new NullPointerException();
// 第四步
final Entry<K,V> getEntryUsingComparator(Object key) {
@SuppressWarnings("unchecked")
K k = (K) key;
Comparator<? super K> cpr = comparator;
if (cpr != null) {
Entry<K,V> p = root; // set 集合的第一个root元素
while (p != null) {
int cmp = cpr.compare(k, p.key); // 进入到自己自定义的比较器
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
// 当存入的对象根据自定义的比较器得到 a.count == b.count 的时候
// >> cmp == 0
// 表示已经存在这样一个值, 返回该值.
}
}
return null;
}
// 第五步
Set<sNode> s = new TreeSet<>(new Comparator<sNode>() {
@Override
public int compare(sNode o1, sNode o2) {
return o1.count - o2.count; // return 0; >> 表示存在
}
});
总结:
所以比较器返回 0 的时候, 表示重复了,不会插入
比较器返回 不等于 0 的时候,会根据 return > 0 向后面迭代, return < 0 向前面迭代 >> 如果最后 p 迭代到 null >> 表示set中没有重复元素, 可以插入.
TreeSet 是通过比较器 compareTo 返回值来进行去重的。
HashSet 是通过 hashCode and equals
小贴士: 个人理解, 可能存在错误。