8

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.

2
  • HashSet<Object>? is your Employee object comparable? Just make sure the equals report objects with same name as equal. Commented Oct 19, 2018 at 8:35
  • 1
    You should sort the Employee List first. Commented Oct 19, 2018 at 8:37

4 Answers 4

11

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.

Sign up to request clarification or add additional context in comments.

Comments

5

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))));

Comments

1

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 :)

Comments

0

you can add values in a Map<String, Employee> (where string is name) only if age is greater of equals than the one in the map.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.