javacavaj
javacavaj

Reputation: 2971

Java - Distinct List of Objects

I have a list/collection of objects that may or may not have the same property values. What's the easiest way to get a distinct list of the objects with equal properties? Is one collection type best suited for this purpose? For example, in C# I could do something like the following with LINQ.

var recipients = (from recipient in recipientList
                 select recipient).Distinct();

My initial thought was to use lambdaj (link text), but it doesn't appear to support this.

Upvotes: 37

Views: 121316

Answers (9)

Craig P. Motlin
Craig P. Motlin

Reputation: 26758

If you're using Eclipse Collections, you can use the method distinct().

ListIterable<Integer> integers = Lists.mutable.with(1, 3, 1, 2, 2, 1);
Assert.assertEquals(
    Lists.mutable.with(1, 3, 2),
    integers.distinct());

The advantage of using distinct() instead of converting to a Set and then back to a List is that distinct() preserves the order of the original List, retaining the first occurrence of each element. It's implemented by using both a Set and a List.

MutableSet<T> seenSoFar = Sets.mutable.with();
int size = list.size();
for (int i = 0; i < size; i++)
{
    T item = list.get(i);
    if (seenSoFar.add(item))
    {
        targetCollection.add(item);
    }
}
return targetCollection;

If you cannot convert your original List into an Eclipse Collections type, you can use ListAdapter to get the same API.

MutableList<Integer> distinct = ListAdapter.adapt(integers).distinct();

Note: I am a committer for Eclipse Collections.

Upvotes: 7

Robert Munteanu
Robert Munteanu

Reputation: 68328

Place them in a TreeSet which holds a custom Comparator, which checks the properties you need:

SortedSet<MyObject> set = new TreeSet<MyObject>(new Comparator<MyObject>(){

    public int compare(MyObject o1, MyObject o2) {
         // return 0 if objects are equal in terms of your properties
    }
});

set.addAll(myList); // eliminate duplicates

Upvotes: 25

Dexter Legaspi
Dexter Legaspi

Reputation: 3312

Java 8:

recipients = recipients.stream()
    .distinct()
    .collect(Collectors.toList());

See java.util.stream.Stream#distinct.

Upvotes: 17

rudnev
rudnev

Reputation: 2370

order preserving version of the above response

return new ArrayList(new LinkedHashSet(recipients));

Upvotes: 11

Mario Fusco
Mario Fusco

Reputation: 13768

Actually lambdaj implements this feature through the selectDistinctArgument method

http://lambdaj.googlecode.com/svn/trunk/html/apidocs/ch/lambdaj/Lambda.html#selectDistinctArgument(java.lang.Object,%20A)

Upvotes: 2

Chase Seibert
Chase Seibert

Reputation: 15861

return new ArrayList(new HashSet(recipients));

Upvotes: 50

glmxndr
glmxndr

Reputation: 46616

Use an implementation of the interface Set<T> (class T may need a custom .equals() method, and you may have to implement that .equals() yourself). Typically a HashSet does it out of the box : it uses Object.hashCode() and Object.equals() method to compare objects. That should be unique enough for simple objects. If not, you'll have to implement T.equals() and T.hashCode() accordingly.

See Gaurav Saini's comment below for libraries helping to implement equals and hashcode.

Upvotes: 33

Apocalisp
Apocalisp

Reputation: 35044

The ordinary way of doing this would be to convert to a Set, then back to a List. But you can get fancy with Functional Java. If you liked Lamdaj, you'll love FJ.

recipients = recipients
             .sort(recipientOrd)
             .group(recipientOrd.equal())
             .map(List.<Recipient>head_());

You'll need to have defined an ordering for recipients, recipientOrd. Something like:

Ord<Recipient> recipientOrd = ord(new F2<Recipient, Recipient, Ordering>() {
  public Ordering f(Recipient r1, Recipient r2) {
    return stringOrd.compare(r1.getEmailAddress(), r2.getEmailAddress());
  }
});

Works even if you don't have control of equals() and hashCode() on the Recipient class.

Upvotes: 3

Steve Kuo
Steve Kuo

Reputation: 63134

You can use a Set. There's couple of implementations:

  • HashSet uses an object's hashCode and equals.
  • TreeSet uses compareTo (defined by Comparable) or compare (defined by Comparator). Keep in mind that the comparison must be consistent with equals. See TreeSet JavaDocs for more info.

Also keep in mind that if you override equals you must override hashCode such that two equals objects has the same hash code.

Upvotes: 4

Related Questions