文档章节

TreeSet(有序集合)对Comparable元素的排序(或使用Comparator)与元素equals方法的关系

猪刚烈
 猪刚烈
发布于 2014/10/12 11:40
字数 792
阅读 30
收藏 0

这是一个非常基础的问题,但是实际编程中还是比较容易被忽视而导致一些看似奇怪的bug,本文对该问题进行一个小结。

我们知道,Set集合的维护的元素是唯一的,不会出现两个一样的元素,这是通过元素的equals和hashCode方法来判定的。而对于TreeSet来说,它本身除了是一个Set集合,同时还会依据一个Comparator或是Comparable接口对元素进行排序。我们就以Comparable的compareTo方法为例,当这个方法返回0是表示的也是“两个元素相等”,那么这就有可能与equals方法产生冲突,那么TreeSet是如何处理compareTo和equals方法两者之间的关系呢?


先不管两者发生冲突时的处理方法,作为良好的编程实践,我们应该首先保证equals,hashCode和compareTo三者的行为是一致的,这样才不会出现怪异的问题。接下来我们看一下当equals和compareTo行为不一致时TreeSet是如何处理的。从TreeSet文档上我们可以找到以下一段描述:

假定使用 Comparator c 将满足 (a.equals(b) && c.compare(a, b) != 0) 的两个元素 a 和 b 添加到一个空 TreeSet 中,则第二个 add 操作将返回 true(树 set 的大小将会增加),因为从树 set 的角度来看,a 和 b 是不相等的,即使这与 Set.add 方法的规范相反。

在Comparable的文档上我们可以找到另一种描述:

如果将两个键 a 和 b 添加到没有使用显式比较器的有序集合中,使 (!a.equals(b) && a.compareTo(b) == 0),那么第二个 add 操作将返回 false(有序集合的大小没有增加),因为从有序集合的角度来看,a 和 b 是相等的。

很明确,两段说明从一正一反两个方向描述了一个一致的原则:是否能加入一个有序集合是由equals方法决定的,但是equals为true,compare不为0的元素加入到集合中后,在排序上会显得怪异。


让我们有一段程序来验证这个问题:


import java.util.PriorityQueue;
import java.util.TreeSet;

public class Test2 {
    public static void main(String[] args) {
        TreeSet<Obj> s1 = new TreeSet<Obj>();
        s1.add(new Obj(1, 1));
        s1.add(new Obj(1, 2));
        for (Obj obj : s1) {
            System.out.println(obj);
        }
        System.out.println("--------------------------");
        TreeSet<Obj> s2 = new TreeSet<Obj>();
        s2.add(new Obj(1, 1));
        s2.add(new Obj(2, 1));
        for (Obj obj : s2) {
            System.out.println(obj);
        }
    }
    public static class Obj implements Comparable<Obj> {
        int a;
        int b;
        public Obj(int a, int b) {
            this.a = a;
            this.b = b;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Obj obj = (Obj) o;

            if (a != obj.a) return false;

            return true;
        }

        @Override
        public int hashCode() {
            return a;
        }

        @Override
        public String toString() {
            return "Obj{" +
                    "a=" + a +
                    ", b=" + b +
                    '}';
        }
        @Override
        public int compareTo(Obj o) {
            return b - o.getB();
        }
        public int getA() {
            return a;
        }
        public void setA(int a) {
            this.a = a;
        }
        public int getB() {
            return b;
        }
        public void setB(int b) {
            this.b = b;
        }
    }
}

程序的运行输出是:

Obj{a=1, b=1}
Obj{a=1, b=2}
--------------------------
Obj{a=1, b=1}

不用再多说明,s1印证了第一段描述,s2印证了第二段描述。


本文转载自:http://blog.csdn.net/bluishglc/article/details/20212599

共有 人打赏支持
猪刚烈
粉丝 22
博文 708
码字总数 110
作品 1
海淀
程序员
私信 提问
java集合框架总结(五)

一、Set接口 概述 Set 接口继承 Collection 接口,而且它不允许集合中存在重复项,每个具体的 Set 实现类 依赖添加的对象的 equals()方法来检查独一性。Set接口没有引入新方法,所以Set就是一...

hapier
2016/09/08
24
0
Java集合框架总结(3)——TreeSet类的排序问题

TreeSet支持两种排序方法:自然排序和定制排序。TreeSet默认采用自然排序。 1、自然排序 TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间大小关系,然后将集合元素按升序排...

dong.li
2012/04/24
0
0
第8篇 Java中的集合(Set)

Java 集合的 Set 接口 Set类型与List类型的区别 Set: 无序、不可重复 List: 有序、可重复 1、HashSet HashSet的存储结构:HashMap 特点: HashSet通过比较存放的哈希码(hashCode)来确定对...

Zero_Yi
07/17
0
0
集合(三):Set

一:java.util.Set(interface)  Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。 接下来将简单介绍Set下的几个实现类,...

牧羊人Berg
2016/06/08
35
0
Java程序员从笨鸟到菜鸟之(五)java开发常用类(包装,数字处理集合等)(下)

本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188 写在前面:由于前天项目老师建设局的项目快到验收阶段,所以,前天晚上通宵,昨天睡了大半天,下午我们宿舍聚...

长平狐
2012/11/12
54
0

没有更多内容

加载失败,请刷新页面

加载更多

Kubernetes里的secret最基本的用法

Secret解决了密码、token、密钥等敏感数据的配置问题,使用Secret可以避免把这些敏感数据以明文的形式暴露到镜像或者Pod Spec中。 Secret可以以Volume或者环境变量的方式使用。 使用如下命令...

JerryWang_SAP
昨天
1
0
可重入锁和非可重入锁

广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。 可重入锁: ReentrantLoc...

狼王黄师傅
昨天
1
0
2018-11-20学习笔记

1. python数据类型: 给变量赋值什么样的值,变量就是什么样的类型 给变量赋值整数,变量就是整数类型 给变量赋值字符串,变量就是字符串类型 123 和“123”一样吗? 在python中 单引号 与双...

laoba
昨天
1
0
使用 React 和 Vue 创建相同的应用,他们有什么差异?

在工作中应用 Vue 之后,我对它有了相当深刻的理解。 不过,俗话说「外国的月亮比较圆」,我好奇「外国的」 React 是怎么样的。 我阅读了 React 文档并观看了一些教程视频,虽然它们很棒,但...

阿K1225
昨天
2
0
2天闭门培训|以太坊智能合约从入门到实战(北京)

2天培训 16个课时 探寻技术原理,精通以太坊智能合约开发 以太坊智能合约是现在应用的最广泛的区块链应用开发方式,HiBlock区块链社区针对以太坊智能合约的学习特别推出2天闭门研修班,通过2...

HiBlock
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部