TreeSet(插入后默认升序) 去重原理 原来和 HashSet 不一样


太有意思了
时间: 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







小贴士: 个人理解, 可能存在错误。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值