按一个值对java对象集合进行排序,并由另一个值保持唯一

时间:2012-05-09 23:27:57

标签: java collections arraylist comparator treeset

我需要按Integer值“level”对java对象集合进行排序。我还需要通过“title”确定此集合是否已包含对象。

我认为集合的最佳选择是TreeSet,它具有一组有序的唯一值。

我有一个带有“level”和“title属性的对象。它实现了可比性:

它重写了Equals方法(用于检查对象是否已经通过“title”包含在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;
}

当我尝试从可能具有重复值的arraylist中向列表添加值时。包含似乎不起作用,并且无论如何都将对象添加到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 个答案:

答案 0 :(得分:3)

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

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

接下来,您应该使用HashSet并使用Set sortedSet = new TreeSet(set);进行排序。一旦你这样做,它应该都可以正常工作。

原因是HashTables依赖的事实是,如果两个对象是equal(),那么他们的hashCode() 相等。以下是hashCode()

的javadoc的摘录
  

hashCode的一般合约是:

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

答案 1 :(得分: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;
}

另外,正如波希米亚人所提到的,如果你要覆盖equals(),你应该覆盖hashCode(),但这不是你的TreeSet允许你添加重复项的原因。

答案 2 :(得分:0)

RTFM:D TreeSet的javadoc明确指出: “请注意,如果要正确实现Set接口,那么由一个集合维护的排序(无论是否提供显式比较器)必须与equals一致”

你的等于和比较器是不一致的。你需要添加的那个,然后你必须对它进行排序。

您可能需要为您的用例创建自己的实现

正如其他人所说:如果你改变了等号,总是改变哈希码。

2个相等的对象必须产生相同的哈希码。

答案 3 :(得分: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;
}

在张贴之前没有看到抢劫的答案。