Java TreeSet:remove和contains()不起作用

时间:2013-06-06 19:07:19

标签: java collections set

我已经向TreeSet添加了一些简单的对象,但是当我调用TreeSet的remove()和contains()方法时,它们不起作用。但是,当我遍历集合时,对象被打印出来。当对象唯一性基于对象名称属性时,应将Employee对象添加到集合中。 Id属性是应该排序的值,但不是唯一的。

public class Employee {
    private String name;
    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

 // Two objects are considered equal if their names are equal
    @Override
    public boolean equals(Object o) {
    if (o == null)
        return false;
    if (this == o)
        return true; 
    if (o.getClass() == this.getClass()) {
        Employee p = ( Employee) o;
        if (p.getName() != null && this.getName() != null)
        return this.getName().equals(p.getName());
        else
        return false;
    } else {
        return false;
    }
    }
} 

//*******************************************************

public class EmployeeComp implements Comparator<Employee> {

    // Sort Ids, but allow duplicates, hence this function is never returning 0
    @Override
    public int compare(Employee p1, Employee p2) {
    int re = 0;

    boolean scoreLt = (p1.getId() > p2.getId());
    boolean scoreGt = (p1.getId() < p2.getId());

    if(scoreLt)
        re = -1;
    if(scoreGt)
        re = 1;
    else 
        re = -1;                       
         return re;                 
    }    
}
//*******************************************************
// Collection shall store unique names with ordered values like:
// Alex, 923
// Toni, 728
// Eddi, 232
// Peter, 232
// Eddi, 156  *** not allowed
import java.util.TreeSet;


public class Main {
    private static EmployeeComp comp = new EmployeeComp(); 
    private static TreeSet<Employee> employees = new TreeSet<Employee>(comp); 

    public static void main(String[] args) {

    Employee p1 = new Employee();
    p1.setName("Eddi");
    p1.setId(232);

    Employee p2 = new Employee();
    p2.setName("Toni");
    p2.setId(728);

    Employee p3 = new Employee();
    p3.setName("Peter");
    p3.setId(232);

    Employee p4 = new Employee();
    p4.setName("Alex");
    p4.setId(923);

    employees.add(p1);
    employees.add(p2);
    employees.add(p3);
    employees.add(p4);

    // Here, contains() and remove() should check the object address
    // and not perform their actions based on compareTo

       } 
}

2 个答案:

答案 0 :(得分:26)

TreeSet根据Comparable的结果插入/移除,而不是.equals() / .hashCode()

这意味着,BTW,Set的对象确实实现了Comparable(如果他们没有,那么每次你尝试并插入一个成员时,你都会受到欢迎使用ClassCastException)。

为了更准确,TreeSetSortedSet的实现。

如果您想要.equals() / .hashCode()兼容的设置,请使用HashSet

为了说明,以下是BigDecimal(几小时前发布的here)的情况:

final BigDecimal one = new BigDecimal("1");
final BigDecimal oneDotZero = new BigDecimal("1.0");

final Set<BigDecimal> hashSet = new HashSet<>();
// BigDecimal implements Comparable of itself, so we can use that
final Set<BigDecimal> treeSet = new TreeSet<>();

hashSet.add(one);
hashSet.add(oneDotZero);
// hashSet's size is 2: one.equals(oneDotZero) == false

treeSet.add(one);
treeSet.add(oneDotZero);
// treeSet's size is... 1! one.compareTo(oneDotZero) == 0

引用Comparable的javadoc,表示BigDecimal的{​​{1}}“与.compareTo()不一致”。

**编辑**关于OP想要什么:

  • .equals(),不接受重复的名称;
  • Collection的排序视图,它将根据用户的ID进行排序。

如上所述,你不能拥有一个同时兼顾的集合。解决方案:

  • 为第一个,Collection;
  • 为第二个,将该副本设置为HashSet,然后使用ArrayList

这意味着Collections.sort().equals()必须仅对名称起作用,而自定义.hashCode()将对ID进行操作。 Comparator别无选择,只能自定义,因为它是一个比较器,在任何情况下都不会与Comparator一致。

关于提议的代码,存在问题。

首先:.equals()会覆盖Employee但不覆盖.equals()。因此,.hashCode()打破了Employee契约(其中一部分是如果两个对象相等,则它们必须具有相同的哈希码)。更重要的是,.equals()对于.hashCode()工作至关重要。修正:

HashSet

第二:比较器与@Override public int hashCode() { return name == null ? 0 : name.hashCode(); } @Override public boolean equals(final Object obj) { if (obj == null) return false; if (this == obj) return false; if (!(obj instanceof Employee)) return false; final Employee other = (Employee) obj; return name == null ? other.name == null : name.equals(other.name); } 一样破碎,因为它违反了Employee合同(适用于任何Comparatoro1o2)。修正:

o1.compareTo(o2) == - o2.compareTo(o1)

然后,如何获取集合的排序副本:

public final class EmployeeComp
    implements Comparator<Employee>
{
    @Override
    public int compare(final Employee o1, final Employee o2)
    {
        final int id1 = o1.getId(), id2 = o2.getId();
        if (id1 == id2)
            return 0;
        return id1 > id2 ? 1 : -1;
    }
}

DONE。

答案 1 :(得分:3)

你的问题是概念性的。

如果需要排序的唯一对象集合:TreeSet
如果你想要一个排序的集合,不同的对象可以有相同的比较值用于排序:PriorityQueue

顺便提一下,PriorityList中的方法比TreeSet上的方法更适合第二种情况的通常需要。我曾经认为它是TreeSet的缺点。例如,从集合中取出第一个项目。

希望有所帮助: - )