3

我需要按整数值“级别”对 Java 对象集合进行排序。我还需要通过“标题”确定此集合是否已经包含一个对象。

我相信集合的最佳选择是具有一组有序唯一值的 TreeSet。

我有一个具有“级别”和“标题”属性的对象。它的实现方式如下:

它覆盖 Equals 方法(用于检查对象是否已通过“标题”包含在 TreeSet 中。

代码如下所示:

@Override
public boolean equals(Object arg0) {

    Artifact obj = (Artifact) arg0;

    if (this.getTitle().equals(obj.getTitle())) { 
        return true;
    }

    return false;
}

@Override
public int compareTo(Artifact aThat) {  

    final int BEFORE = -1;
    final int EQUAL = 0;
    final int AFTER = 1;

    if (this == aThat) return EQUAL;

    if (this.level < aThat.level) return BEFORE;
    if (this.level > aThat.level) return AFTER;

    assert this.equals(aThat) : "compareTo inconsistent with equals.";
    return EQUAL;
}

当我尝试将具有可能重复值的数组列表中的值添加到列表中时。包含似乎不起作用,并且无论如何都会将对象添加到 TreeSet 中。这是代码:

TreeSet<Artifact> subsetOfArtifacts = new TreeSet<Artifact>();

ArrayList<Artifact> allArtifacts = getArtifacts(); 
Iterator<Artifact> allArtifactsIter = allArtifacts.iterator();

while (allArtifactsIter.hasNext()) {
    Artifact artifact = (Artifact) allArtifactsIter.next();
    if (!subsetOfArtifacts.contains(artifact)) {
        subsetOfArtifacts.add(artifact);
    }
 }

理想情况下,我希望有一个按级别排序的所有独特工件的列表。我该如何做到这一点?

4

4 回答 4

3

如果你覆盖equals()你也应该覆盖hashCode()!否则,集合的行为,尤其是集合,是未定义的。您应该将此方法添加到您的类中:

@Override
public int hashCode() {
    return title.hashCode();
}

接下来,您应该改用 HashSet 并Set sortedSet = new TreeSet(set);用于排序。一旦你这样做,它应该一切正常。

原因是 HashTables 依赖于这样一个事实:如果两个对象是,equal()那么它们hashCode()也是相等的。这是javadoc的摘录hashCode()

hashCode 的一般合约是:

  • 每当在 Java 应用程序执行期间对同一个对象多次调用它时,hashCode 方法必须始终返回相同的整数,前提是没有修改对象上的 equals 比较中使用的信息。该整数不需要从应用程序的一次执行到同一应用程序的另一次执行保持一致。
  • 如果两个对象根据 equals(Object) 方法相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。
  • 如果根据 equals(java.lang.Object) 方法,如果两个对象不相等,则不需要对两个对象中的每一个调用 hashCode 方法都必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
于 2012-05-09T23:37:58.130 回答
1

不仅需要 compareTo 来比较级别,还需要它来比较标题,因为 equals 比较的是标题。

public int compareTo(Artifact aThat) {

    final int BEFORE = -1;
    final int EQUAL = 0;
    final int AFTER = 1;

    if ( this == aThat ) return EQUAL;

    if (this.level < aThat.level) return BEFORE;
    if (this.level > aThat.level) return AFTER;
    return this.getTitle().compareTo(aThat.getTitle());

//        assert this.equals(aThat) : "compareTo inconsistent with equals.";

//    return EQUAL;
}

此外,正如 Bohemian 所提到的,如果要覆盖 equals(),则应该覆盖 hashCode(),但这不是 TreeSet 允许添加重复项的原因。

于 2012-05-09T23:37:28.620 回答
0

RTFM :D javadoc for TreeSet clearly states: "Note that the ordering maintained by a set (whether or not an explicit comparator is provided) must be consistent with equals if it is to correctly implement the Set interface"

your equals and comparator are inconsistent. the one you need to add and then you will have to sort it.

You might need to create your own implementation for your use case

and as the other guys said: if you change the equals, always change the hashcode.

2 equal objects must produce equal hashcode.

于 2012-05-09T23:53:30.663 回答
0

您的 compareTo 应该首先检查级别,然后是标题,如果您希望它先按级别排序,然后按标题排序,并且只有在级别和标题相等时才返回相等。像这样的东西:

@Override
public int compareTo(Artifact aThat) 
{
    final int BEFORE = -1;
    final int EQUAL = 0;
    final int AFTER = 1;

    if ( this == aThat ) return EQUAL;

    if( this.level < aThat.level ) return BEFORE;
    if( this.level > aThat.level ) return AFTER;

    int compare = this.getTitle().compareTo(aThat.getTitle());

    if( compare != EQUAL ) return compare;

    assert this.equals(aThat) : "compareTo inconsistent with equals.";

    return EQUAL;
}

在发布之前没有看到 rob 的答案。

于 2012-05-09T23:59:41.280 回答