TreeSet 正在向 Set 添加重复值

sca*_*ter 5 java collections

我正在解决一个问题。我必须创建一个自定义 Employee 对象的 TreeSet,其中数据应按薪水排序,但员工 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;
        }
    }
}   
Run Code Online (Sandbox Code Playgroud)

程序主要方法

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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

上述程序的输出如下

2 jim 10000.0
1 john 20000.0
3 jack 30000.0
2 ted 35000.0
9 mike 50000.0
3 jason 85000.0
Run Code Online (Sandbox Code Playgroud)

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

有人可以指导我哪里出错了吗?

Tur*_*g85 8

的实施Comparable违反了的合同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));
Run Code Online (Sandbox Code Playgroud)

导致以下输出:

jim compare to ted: 0
john compare to jim: 1
john compare to ted: -1
Run Code Online (Sandbox Code Playgroud)

Ideone demo

我们可以通过从 - 方法中删除薪水compareTo并仅按以下顺序来解决此问题empId

@Override
public int compareTo(Employee e) {
  return Integer.compare(empId, e.empId);
}
Run Code Online (Sandbox Code Playgroud)

Ideone demo

  • @BasilBourque“与等于一致”问题与此无关。正如该答案正确描述的那样,主要问题是需要满足合同的基本原则。 (3认同)
  • @scarletspeedster 确定集合中对象唯一性的标准必须与确定对象排序的标准*相同*。听起来您想要基于员工 ID 的唯一性,但基于工资进行排序。您无法编写一个单一、正确的“compareTo”方法来同时执行这两项操作。如果这就是您想要的,您需要使用“TreeSet”以外的东西或除了“TreeSet”之外的其他东西。 (3认同)