简体   繁体   中英

Remove duplicates from List<Object> based on condition

Starting point:

public class Employee {
    private String id;
    private String name;
    private String age;
}

I have a list of Employee: List<Employee> employee;

Employee examples from the list:

{id="1", name="John", age=10}
{id="2", name="Ana", age=12}
{id="3", name="John", age=23}
{id="4", name="John", age=14}

Let's assume that age is unique.

How can I remove all duplicates from the list based on the name property and to keep in the output the entry with the largest age ?

The output should look like:

{id="2", name="Ana", age=12}
{id="3", name="John", age=23}

The way I tried :

HashSet<Object> temp = new HashSet<>();
employee.removeIf(e->!temp.add(e.getName()));

..but the this way the first match will be kept in employee

{id="1", name="John", age=10}
{id="2", name="Ana", age=12}

...and I have no idea how to put an another condition here to keep the one with the largest age .

Here's a way that groups elements by name and reduces groups by selecting the one with max age :

List<Employee> uniqueEmployees = employees.stream()
            .collect(Collectors.groupingBy(Employee::getName,
                    Collectors.maxBy(Comparator.comparing(Employee::getAge))))
        .values()
        .stream()
        .map(Optional::get)
        .collect(Collectors.toList());

Which returns [[id=2, name=Ana, age=12], [id=3, name=John, age=23]] with your test data.

Apart from the accepted answer , here are two variants:

Collection<Employee> employeesWithMaxAge = employees.stream()
    .collect(Collectors.toMap(
             Employee::getName,
             Function.identity(),
             BinaryOperator.maxBy(Comparator.comparing(Employee::getAge))))
    .values();

This one uses Collectors.toMap to group employees by name, letting Employee instances as the values. If there are employees with the same name, the 3rd argument (which is a binary operator), selects the employee that has max age.

The other variant does the same, but doesn't use streams:

Map<String, Employee> map = new LinkedHashMap<>(); // preserves insertion order
employees.forEach(e -> map.merge(
        e.getName(), 
        e, 
        (e1, e2) -> e1.getAge() > e2.getAge() ? e1 : e2));

Or, with BinaryOperator.maxBy :

Map<String, Employee> map = new LinkedHashMap<>(); // preserves insertion order
employees.forEach(e -> map.merge(
        e.getName(), 
        e, 
        BinaryOperator.maxBy(Comparator.comparing(Employee::getAge))));

ernest_k answer is great but if you maybe want to avoid adding duplicates you can use this:

public void addToEmployees(Employee e) {
    Optional<Employee> alreadyAdded = employees.stream().filter(employee -> employee.getName().equals(e.getName())).findFirst();
    if(alreadyAdded.isPresent()) {
        updateAgeIfNeeded(alreadyAdded.get(), e);
    }else {
        employees.add(e);
    }
}

public void updateAgeIfNeeded(Employee alreadyAdded, Employee newlyRequested) {
    if(Integer.valueOf(newlyRequested.getAge()) > Integer.valueOf(alreadyAdded.getAge())) {
        alreadyAdded.setAge(newlyRequested.getAge());
    }
}

Just use addToEmployees method to add Employee to your list.

You can also create class extending ArrayList and override add method like so and then use your own list :)

只有当age大于等于地图中的年龄时Map<String, Employee>才能在Map<String, Employee> (其中string是name)中添加值。

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