-1

How can I get all the distinct objects in a list using java 8 streams? It has to compare all the fields of objects with each other. For example

Person a = Person("nameA", "35") and Person b = Person("nameA", "35")

should be equal. What I've attempted personList.stream().distinct().collect(Collectors.toList()) is returning a list of 2 persons, instead of only 1.

Nazerke
  • 2,098
  • 7
  • 37
  • 57

2 Answers2

1

You'll need to override equals and hashcode to get the expected result.

Example:

class Person {
    private String name;
    private String theOtherField; 

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

        Person person = (Person) o;

        return (name != null ? name.equals(person.name) : person.name == null) && 
                (theOtherField != null ? theOtherField.equals(person.theOtherField) : 
                        person.theOtherField == null);
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (theOtherField != null ? theOtherField.hashCode() : 0);
        return result;
    }

    public Person(String name, String theOtherField) {
        this.name = name;
        this.theOtherField = theOtherField;
    }
}

Now, assuming you have a list of people like this for example:

 List<Person> personList = Arrays.asList(new Person("nameA", "35"),
               new  Person("nameA", "35"));

Performing the distinct operation should yield a list with one element.

List<Person> distinctPeople = 
           personList.stream()
                     .distinct()
                     .collect(Collectors.toList());

or you can collect to a Set implementation without using distinct like this:

 Set<Person> distinctPeople = new HashSet<>(personList);

Note, I've named one of the properties in the Person class theOtherField because I don't know whether it represents the person's age or not as ideally a person's age should be an int not String.

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
-1

If you can you rely on the equals and hashCode method of the Person class like @Eran wrote in the comment, then you can use your code and it will do the job as intended. If you cannot change the Person class for some reason, then you need to use a collector with "group by" and specify your key.

Here is an example of how to use "group by" to create your own grouping key:

Person person1 = new Person("John", "Doe");
Person person2 = new Person("John", "Doe");
Person person3 = new Person("Mary", "Doe");
Person person4 = new Person("Mary", "Doe");
Person person5 = new Person("john", "Smith");
List<Person> persons = Arrays.asList(person1, person2, person3, person4, person5);

// Creates your own equality using "group by". Here we are using first and last name concatenation
final Map<String, List<Person>> collect = persons.stream()
        .collect(Collectors.groupingBy(o -> o.getFirstName() + o.getLastName()));
// Get the values and extract the first element to build a distinct list.
final List<Person> collect1 = collect.values().stream().map(ps -> ps.get(0)).collect(Collectors.toList());
System.out.println(collect1.size());

This prints out:

3
gil.fernandes
  • 12,978
  • 5
  • 63
  • 76
  • 1
    It's not a good idea to group by concatenation. "al" + "andrews" equals "alan" + "drews". If you have to go this route, it's better to group by `Arrays.asList(first, last)` – Misha Apr 08 '18 at 22:26