我正在解决一个问题.我必须创建一个自定义Employee对象的树集,其中的数据应该按薪水排序,但Employee Id必须是唯一的.我知道equals()和hashCode()方法不适用于TreeSet,我们需要在compareTo()方法中编写对象是否相等的逻辑.我正在判断两个员工Id是否相等,然后返回0,这意味着不应添加对象.

但是,由于拥有相同员工Id的员工也在增加,输出并没有达到预期.我试图调试这个,但我没有得到正确的答案.

这是密码.

public class Employee implements Comparable<Employee>{
    int empId;
    String empName;
    double salary;
    
    public Employee() {
        super();
    }

    public Employee(int empId, String empName, double salary) {
        super();
        this.empId = empId;
        this.empName = empName;
        this.salary = salary;
    }
    
    @Override
    public int hashCode() {
        return empId;
    }
    
    @Override
    public boolean equals(Object o) {
        if(this == o) return true;
        if(o == null || this.getClass() != o.getClass()) return false;
        
        Employee e = (Employee) o;
        return (this.empId == e.empId);
    }
    
    @Override
    public String toString() {
        return empId + " " + empName + " " + salary;
    }
    
    @Override
    public int compareTo(Employee e) {
        if(empId == e.empId) 
            return 0;
        
        if(this.salary < e.salary) {
            return -1;
        }
        else {
            return 1;
        }
    }
}   

程序设计的主要方法

public static void main(String[] args) {
        
        TreeSet<Employee> eSet = new TreeSet<>();
        
        eSet.add(new Employee(1, "john", 20000));
        eSet.add(new Employee(2, "jim", 10000));
        eSet.add(new Employee(9, "mike", 50000));
        eSet.add(new Employee(3, "jack", 30000));
        eSet.add(new Employee(3, "david", 40000));
        eSet.add(new Employee(9, "liam", 80000));
        eSet.add(new Employee(9, "brad", 89000));
        eSet.add(new Employee(3, "jason", 85000));
        eSet.add(new Employee(2, "ted", 35000));
        
        for(Employee e: eSet) {
            System.out.println(e);
        }
    }

上述程序的输出结果如下:

2 jim 10000.0
1 john 20000.0
3 jack 30000.0
2 ted 35000.0
9 mike 50000.0
3 jason 85000.0

在这里,您可以看到具有相同员工Id的员工被添加到树集中,这是不应该发生的.如果我使用的是HashSet,问题就解决了,但我必须使用TreeSet来实现它,以获得排序的行为.

有人能告诉我哪里出了问题吗?

推荐答案

Comparable的实施违反了contract of Comparable::compareTo,尤其是本部分:

最后,实现者必须确保x.compareTo(y)==0意味着signum(x.compareTo(z)) == signum(y.compareTo(z)),对于所有z.

我们可以用以下代码演示这种违规行为:

final Employee jim = new Employee(2, "jim", 10_000);
final Employee ted = new Employee(2, "ted", 35_000);
final Employee john = new Employee(9, "john", 20_000);

System.out.println("jim compare to ted: " + jim.compareTo(ted));
System.out.println("john compare to jim: " + john.compareTo(jim));
System.out.println("john compare to ted: " + john.compareTo(ted));

导致以下输出:

jim compare to ted: 0
john compare to jim: 1
john compare to ted: -1

Ideone demo

我们可以通过从compareTo方法中删除工资,只在empId之前订购来解决这个问题:

@Override
public int compareTo(Employee e) {
  return Integer.compare(empId, e.empId);
}

Ideone demo

Java相关问答推荐

更新我们的一个文物后出现了严重的符号引用错误

如何使用解析器组合子解析Java数组类型签名?

Java应用程序崩溃时试图读取联系人从电话

转换为Biggram

空手道比赛条件

Java 8中的多个字段和计数

Kubernetes的Java客户端检索状态.处于终止状态的Pod的阶段';正在运行';

R.id.main给我一个红色错误,无法解析MainActivity.java中的符号main

Java LocalTime.parse在本地PC上的Spring Boot中工作,但在Docker容器中不工作

蒙蒂霍尔比赛结果不正确

MySQL数据库中未应用具有Spring数据的唯一约束

如何让JVM在SIGSEGV崩溃后快速退出?

由于在生成器模式中使用泛型,lambda表达式中的返回类型错误

无法将GSON导入到我的JavaFX Maven项目

通过Java列表中的某些字段搜索值

为了安全起见,有必要复制一份 list 吗?

我的代码是线程安全的吗?[Java、CAS、转账]

OAuth:登录后无法查看Google邮箱地址

如何使用带有可选参数的类生成器?

如何在 WebSphere Application Server 内的托管线程上运行 BatchEE 作业(job)?