Removing duplicates in TreeSet

arraylist, collections, java, treeset

Solution

Originally, this answer neglected the fact that a `TreeSet` does its comparisons based on `compareTo()`, rather than `equals()`. Edits have been made to address this.

You need to define `equals()`, `hashCode()` and `compareTo()` for your `Player` object correctly. (Since it's a `TreeSet` and not a `HashSet`, implementing `hashCode()` isn't so important - but it's good practice.)

Equals and hashCode need to take into account all of the fields. Eclipse can auto-generate one for you that will look similar to this (Source > Generate hashcode and equals).

If you already have a natural sort order that doesn't use all of the fields, then you could supply a custom comparator to your `TreeSet`. However, even if you really only want to sort by a subset of the fields, there's nothing stopping you sorting by all fields (with the uninteresting fields only playing a part of the interesting parts are identical). The important thing to note here is that a `TreeSet` determines equality not by the `equals()` method, but by `compareTo() == 0`.

Here's an example equals():

@Override
public boolean equals(Object obj)
{
  if (this == obj) {
    return true;
  }
  if (obj == null) {
    return false;
  }
  if (getClass() != obj.getClass()) {
    return false;
  }

  Player that = (Player) obj;
  return this.age == that.age &&
         this.height == that.height &&
         this.weight == that.weight &&
         this.games == that.games &&
         this.runs == that.runs &&
         this.dismisses == that.dismisses &&
         this.given.equals(that.given) &&
         this.family.equals(that.family);
}

And here's hashcode:

@Override
public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + this.age;
  result = prime * result + this.dismisses;
  result = prime * result + this.family.hashCode());
  result = prime * result + this.games;
  result = prime * result + this.given.hashCode());
  result = prime * result + this.height;
  result = prime * result + this.runs;
  result = prime * result + this.weight;
  return result;
}

Finally, here's a compareTo:

public int compareTo(Player that)
{
  int result;

  result = this.family.compareTo(that.family); 
  if (result != 0)                              // is the family name different?
  {
    return result;                              // yes ... use it to discriminate
  }

  result = this.given.compareTo(that.given);
  if (result != 0)                              // is the given name different?
  {
    return result;                              // yes ... use it to discriminate
  }

  result = this.age - that.age;                 // is the age different?
  if (result != 0)
  {
    return result;                              // yes ... use it to discriminate
  }

  ... (and so on) ...
  ... with the final one ...

  return this.dismisses - that.dismisses;       // only thing left to discriminate by
}

Problem

I've been using ArrayList for my project to store a cricket team players and order them. I started thinking about using a TreeSet because of its advantage of removing duplicates. However the problem I'm having is that if for example I create the following two players: ``` P p1 = new P("Jack","Daniel",33(age),180(height),78(weight),41(games played),2300 (runs scored),41(dismisses)) P p2 = new P("Jack","Daniel",37(age),185(height),79(weight),45(games played),2560 (runs scored),45(dismisses)) ``` Notice that the two players have the same first and last name, but everything else is different. When I try to add these two players to the TreeSet, it considers them duplicates because of the names similarities and removes the second one. Obviously I don't want this to happen and I want the Set to remove a player only if everything he has is the same as another player, and not just the first and last names. Is there a way of achieving this? Also my TreeSet takes a Player object.

Original source