4

I need to sort a java collection of objects by an Integer value "level". I also need to determine if this collection already contains an object by "title".

I believe the best choice of a collection is a TreeSet to have an ordered set of unique values.

I have an object with the "level" and "title attributes. It implements comparable like so:

It overrides the Equals method (for checking if the object is already contained in the TreeSet by "title".

The code looks like:

@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;
}

When I attempt to add values to the list from an arraylist with possibly duplicate values. The contains seems to not work and objects are added to the TreeSet regardless. Here is the code:

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);
    }
 }

I want to ideally have a list of all unique artifacts sorted by level. How do I accomplish this?

Miguel Rodrigues
  • 917
  • 6
  • 19
Atma
  • 29,141
  • 56
  • 198
  • 299

4 Answers4

3

If you override equals() you should override hashCode() too! Otherwise, behaviour with collections, especially Sets, is undefined. You should add this method to your class:

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

Next, you should use a HashSet instead and use Set sortedSet = new TreeSet(set); for the sorting. Once you do that, it should all work OK.

The reason is that HashTables rely on the fact that if two objects are equal() then their hashCode() is also equal. Here's an excerpt from the javadoc for hashCode()

The general contract of hashCode is:

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.
Bohemian
  • 412,405
  • 93
  • 575
  • 722
1

Not only do you need compareTo to compare the level, but you also need it to compare the title, since equals compares the title.

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;
}

Also, as Bohemian mentions, you should override hashCode() if you're overriding equals(), but that's not why your TreeSet is allowing you to add duplicate items.

rob
  • 6,147
  • 2
  • 37
  • 56
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.

ZeoS
  • 72
  • 1
  • 5
0

Your compareTo should first check level, then title, if you want it to sort first by level then by title, and only return equal if both level and title is equal. Something like this:

@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;
}

Didnt see rob's answer before posting.

Martin
  • 1,130
  • 10
  • 14