How to remove duplicates from a list based on a custom java object not a primitive type?

23,317

Solution 1

The simplest way to remove elements based on a field is as follows (preserving order):

Map<Integer, AwardYearSource> map = new LinkedHashMap<>();
for (AwardYearSource ays : list) {
  map.put(ays.getYear(), ays);
}
list.clear();
list.addAll(map.values());

Solution 2

Another way would be to override hashCode() and equals(Object obj) for your object. Since it just has one field you want to use to determine equality, this is pretty straightforward. Something like:

public boolean equals(Object obj) {
  if (obj == null || !(obj instanceof AwardYearSource)) {
    return false;
  }
  return (this.year == ((AwardYearSource)obj).year);
}
public int hashCode() {
  return this.year;
}

Then you can just stick all of the objects into a Set to remove duplicates:

Set<AwardYearSource> set = new Set<AwardYearSource>();

set.add(new AwardYearSource(2011));
set.add(new AwardYearSource(2012));
set.add(new AwardYearSource(2011));

for (AwardYearSource aws : set) {
  System.out.println(aws.year);
}

Solution 3

Fairly simply. Although something bugs me about the map versions (not that I doubt they'd work, it just seems like overkill, somehow - although this version isn't necessarily any better in that regard).
Answer is functional, and threadsafe (assuming AwardYearSource is immutable).

public static List<AwardYearSource> removeDuplicateYears(
                                          final Collection<AwardYearSource> awards) {
    final ArrayList<AwardYearSource> input = new ArrayList<AwardYearSource>(awards);
    // If there's only one element (or none), guaranteed unique.
    if (input.size() <= 1) {
        return input;
    }
    final HashSet<Integer> years = new HashSet<Integer>(input.size(), 1);
    final Iterator<AwardYearSource> iter = input.iterator();
    while(iter.hasNext()) {
        final AwardYearSource award = iter.next();
        final Integer year = award.getYear();
        if (years.contains(year)) {
            iter.remove();
        } else {
            years.add(year);
        }
    }
    return input;       

}
Share:
23,317
WowBow
Author by

WowBow

Updated on October 18, 2020

Comments

  • WowBow
    WowBow over 3 years

    Before I post this question, I found somehow similar question posted here. But the answer was based on a String. However, I have a different situation here. I am not trying to remove String but another object called AwardYearSource. This class has an int attribute called year. So I want to remove duplicates based on the year. i.e if there is year 2010 mentioned more than once, I want to remove that AwardYearSource object. How can I do that?

  • WowBow
    WowBow about 12 years
    Thank you. It solved my problem. However I changed the first line of your code to Map<Integer, AwardSource> map = new LinkedHashMap<Integer,AwardSource>(); .... otherwise it won't compile.
  • Clockwork-Muse
    Clockwork-Muse about 12 years
    Really? Could you maybe provide more details? Although at least one of the iterators is unnecessary - see some of the other answers. And this would have some nasty side-effects, if used in a threaded context.
  • smichak
    smichak about 12 years
    Maybe you have posted this comment on the wrong answer? I don't see any iterators in my solutions and it's quite thread safe.
  • Clockwork-Muse
    Clockwork-Muse about 12 years
    You explicitly mention iterate, and you're going to be using at least one implicit (compiler generated, in the case of the for-each construct) iterator. If this is being packaged up in it's own method (which it should be), then removing items from the original list is absolutely NOT threadsafe.
  • smichak
    smichak about 12 years
    I don't think thread safety is quite an issue here at least according to the question that was asked. I think the map-based solutions offered by me and others are the most simple ones.
  • ishu
    ishu almost 7 years
    Excellent dude...,
  • Sachin M
    Sachin M over 5 years
    Nice and Easy way... Super