简体   繁体   中英

Get unique items from List of Objects with a Set in Java

my people object looks like this

class People {
   this.name,
   this.height,
   this.age
}

I have a list from a database query like so

List<People> people = DAO.queryAllPeople();

which returns 100's of people

Then I want just people with unique height

    Set<People> uniquePeople = list
                    .stream()
                    .map(people -> people)
                    .filter(Objects::nonNull)
                    .collect( Collectors.toSet() );

But this is returning all objects, is there a way to get people distinct by height?

Edit this is what I want but I want the Person object so I can call get methods when I loop over it

  Set<String> people =      people
                                .stream()
                                .map(People::getHeight)
                                .filter(Objects::nonNull)
                                .collect( Collectors.toSet() );

First, naming a class People is not natural, a better name would be Person .

As, for solving your problem, you can override equals and hashcode for height only like this:

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

     Person person = (Person) o;

     return height == person.height;
}

@Override
public int hashCode() {
     return height;
}

The above assumes height is an int field. if instead, it's Integer , then you'll need to implement it like so:

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

      Person person = (Person) o;

      return height != null ? height.equals(person.height) : person1.height == null;
}

@Override
public int hashCode() {
     return height != null ? height.hashCode() : 0;
}

Now, you can do:

 Set<People> uniquePeople = 
              myList.stream()
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());

or for what ever reason you don't want to override equals and hashcode you can do it with the toMap collector .

Set<Person> values = new HashSet<>(myList.stream()
                .collect(Collectors.toMap(Person::getHeight, Function.identity(),
                        (left, right) -> left))
                .values());

deciphering the above code snippet:

myList.stream()
      .collect(Collectors.toMap(Person::getHeight, Function.identity(),
              (left, right) -> left))
      .values()

This creates a stream from myList collecting it to a map implementation, where Person::getHeight is a function extracting the person height for the map keys, Function.identity() is a function extracting a person object for the map values, (left, right) -> left) is known as the merge function meaning if two given people have the same key (height) we return the first person ( left ). Conversely, (left, right) -> right will return the last person in the case of key conflict.

Lastly, we pass the result of this processing to the HashSet constructor to create a Set<Person> .

Split this task into two subtasks.

First group people by height:

Map<Integer, List<People>> groups = list.stream()
        .collect(Collectors.groupingBy(People::getHeight);

Then find which groups have only one person:

groups.entrySet().stream()
        .filter(e -> e.getValue().size() == 1) // use only groups with one person
        .map(e -> e.getValue().get(0))
        .collect(Collectors.toList());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM