【问题标题】:Why does my TreeSet not add anything beyond the first element?为什么我的 TreeSet 没有在第一个元素之外添加任何内容?
【发布时间】:2014-04-03 02:50:21
【问题描述】:

我有几个数组的形式:

private static String[] patientNames = { "John Lennon", "Paul McCartney", "George Harrison", "Ringo Starr" };

然后我制作一个这样的 TreeSet:

TreeSet<Patient> patTreeSet = new TreeSet<Patient>();

其中 Patient 是制作“Patient”对象的不同类。 然后我循环遍历数组中的每个元素以创建多个患者并将它们添加到我的patTreeSet,如下所示:

for(int i = 0; i< patientNames.length; i++){
     Date dob = date.getDate("MM/dd/yyyy", patientBirthDates[i]);
     Patient p = new PatientImpl(patientNames[i], patientSSN[i], dob);

     patTreeSet.add(p);
}

但是当我去查看我的patTreeSet.size() 时,它只返回“1”——这是为什么呢?

我知道我的对象运行良好,因为当我尝试做同样的事情但使用ArrayList 时,一切正常。所以我猜我用错了 TreeSet。

如果有帮助,Patient 有一个名为 getFirstName() 的方法,当我尝试执行以下操作时:

Iterator<Patient> patItr = patTreeSet.iterator();

while(patItr.hasNext()){
    System.out.println(patItr.next().getFirstName());

}

然后只有“John”打印,显然不应该是这种情况......那么,我完全误用了 TreeSet 吗?

提前感谢您的帮助!

编辑下面

================PatientImpl 类====================

public class PatientImpl implements Patient, Comparable{

    Calendar cal = new GregorianCalendar();
    private String firstName;
    private String lastName;
    private String SSN;
    private Date dob;
    private int age;
    private int thisID;             
    public static int ID = 0;       



    public PatientImpl(String fullName, String SSN, Date dob){

        String[] name = fullName.split(" ");
        firstName = name[0];
        lastName = name[1];

        this.SSN = SSN;

        this.dob = dob;

        thisID = ID += 1;
    }

@Override
    public boolean equals(Object p) {

        //for some reason casting here and reassigning the value of p doesn't take care of the need to cast in the if statement...
        p = (PatientImpl) p;

        Boolean equal = false;
        //make sure p is a patient before we even compare anything
        if (p instanceof Patient) {

            Patient temp = (Patient) p;

            if (this.firstName.equalsIgnoreCase(temp.getFirstName())) {
                if (this.lastName.equalsIgnoreCase(temp.getLastName())) {
                    if (this.SSN.equalsIgnoreCase(temp.getSSN())) {
                        if(this.dob.toString().equalsIgnoreCase(((PatientImpl) p).getDOB().toString())){
                            if(this.getID() == temp.getID()){
                                equal = true;
                            }
                        }
                    }
                }
            }
         }
        return equal;
    }

然后所有的getter都在下面,还有Comparable接口的compareTo()方法

【问题讨论】:

  • 向我们展示您的PatientImpl 课程。
  • 粘贴Patient的代码
  • TreeSet 依赖于您的 Patient/PatientImpl equals/hashCode/compareTo 方法。它们应该根据 Object/Comparable 契约正确实现。
  • 您省略了最重要的部分,即Patient 类的代码。如果Patient 不包含equals()hashCode() 的正确实现,它将不起作用。

标签: java set treeset


【解决方案1】:

如果您将对象放在TreeSet 中,您需要在构造函数中提供Comparator 接口的实现,或者您需要您的对象属于实现Comparable 的类。

您说您从Comparable 接口实现compareTo,但在您的评论中您说您没有,所以我假设您只是在compareTo 方法中使用return 0; 是否正确?这可以解释您的问题,因为 TreeSet 会根据 compareTo 方法结果认为您的所有对象都是“相同的”。

基本上,在TreeSet 中,您的对象按排序顺序维护,排序由 Comparable/Comparator 方法的结果确定。这用于快速查找 TreeSet 中的重复项,并具有额外的好处,即当您迭代 TreeSet 时,您可以按排序顺序获得结果。

TreeSet 的 Javadoc 说:

请注意,由集合维护的顺序(无论是否显式 提供了比较器)必须与equals一致如果它是 正确实现Set接口。

实现这一点的最简单方法是让您的equals 方法调用compareTo 方法并检查结果是否为0

鉴于您的 PatientImpl 课程,我假设您希望先按姓氏对患者进行排序,然后按他们的名字,然后按课程中的其余字段。

您可以像这样实现compareTo 方法:

@Override
public int compareTo(Object o) {
    if (!(o instanceof Patient))
        return -1;
    Patient temp = (Patient) o;
    int r = this.lastName.compareToIgnoreCase(temp.getLastName());
    if (r == 0)
        r = this.firstName.compareToIgnoreCase(temp.getFirstName());
    if (r == 0)
        r = this.SSN.compareToIgnoreCase(temp.getSSN());
    if (r == 0)
        r = this.dob.toString().compareToIgnoreCase(temp.getDOB().toString());
    if (r == 0)
        r = Integer.compare(this.getID(), temp.getID());
    return r;
}

我相信这会解决您描述的问题。 我建议您阅读(Javadoc 或书籍)TreeSetHashSet 以及 equalscompareTohashCode 方法的重要性。 如果您想将您的对象放入 Set 或 Map 中,您需要了解这些才能正确实现。

注意 我将此compareTo 方法基于您的equals 方法。 您通过首先调用 toString 来比较出生日期。这不是一个很好的方法——你可以直接在 java.util.Date 中使用equals 方法。在 compareTo 方法中,问题变得更糟,因为当您按字母顺序对日期进行排序时,日期排序不正确。 java.util.Date 还实现了 Comparable,因此您可以将方法中的比较替换为:

    if (r == 0)
        r = this.dob.compareTo(temp.getDOB());

此外,如果任何字段可能是null,您也需要检查。

【讨论】:

  • Erwin,感谢您抽出宝贵时间提供详尽的答案。它真的很有帮助。你在几个注释上是对的,特别是 - 当我在我的 PatientImpl 类中实现 Comparable 接口时,我让它自动添加 compareTo 方法(返回 0),这使得一切看起来都是平等的。我改变了它,现在它可以工作了!感谢您对日期和其他一切的看法! (如果我有足够的声望点,我会给你的答案加 1!)干杯
  • compareTo 实现最终返回 0。应该是r吧?
  • @jayeffkay 你说得对,有趣的是这已经 3 年没有被抓到了。我还更新了 id 的比较 - 使用 Integer.compare 比减法更好,因为它避免了可能的整数溢出情况。
猜你喜欢
  • 2019-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
相关资源
最近更新 更多