10

I'm trying to retrieve a list of those with lowest salary from a list of employees. So far I've managed to find the employee with lowest salary, but I want to retrieve multiple if several employees have the same salary.

I think the solution is supposed to be in one line. So I can't create a variable with the lowest salary and just check each one for 'Salary less than or equal to Low_Salary'. I tried this, and it worked.

So I believe my problem is that ".min(comparator)" retrieves only one of the lowest.

Comparator<Employee> comparator = Comparator.comparing( Employee :: getSalary);
List<Employee> lowSalary = employees.stream()
               .min(comparator)
               .stream()
               .collect(Collectors.toList());
lowSalary.forEach(System.out::println);
Ravindra Ranwala
  • 20,744
  • 6
  • 45
  • 63
mollasund
  • 103
  • 1
  • 1
  • 5

5 Answers5

13

First create a TreeMap, whose key is the salary. TreeMap sorts it's entries by it's key. Then grab the first entry, which is the entry with the lowest salary and get hold of the values associated with that. This solution iterates over the list only once. Here's how it looks.

List<Employee> empsWithLowestSalary = employees.stream()
    .collect(Collectors.groupingBy(Employee::getSalary, TreeMap::new, Collectors.toList()))
    .firstEntry()
    .getValue();

TreeMap stores map elements in a Red-Black tree. The insertion cost for one element in Red-Black tree is O(Log (n)). Since we are inserting n elements, the total Time complexity of this solution is O(n Log (n)). For the firstEntry(), it takes constant time O(1), since it maintains a pointer to the leftmost and rightmost leaf nodes in the tree respectively. The leftmost node represent the smallest value in the tree whereas the rightmost leaf node represents the highest value.

Just by following this great answer, I thought of writing a custom collector that serves our purpose. This collector iterates over the List only once and it's runtime complexity lies at O(n), which significantly outperforms the above approach. Furthermore it allows you to write your client code in one single statement. Here's how it looks.

static <T> Collector<T, ?, List<T>> minList(Comparator<? super T> comp) {
    return Collector.of(ArrayList::new, (list, t) -> {
        int c;
        if (list.isEmpty() || (c = comp.compare(t, list.get(0))) == 0)
            list.add(t);
        else if (c < 0) {
            /*
             * We have found a smaller element than what we already have. Clear the list and
             * add this smallest element to it.
             */
            list.clear();
            list.add(t);
        }
    }, (list1, list2) -> {
        if (comp.compare(list1.get(0), list2.get(0)) < 0)
            return list1;
        else if (comp.compare(list1.get(0), list2.get(0)) > 0)
            return list2;
        else {
            list1.addAll(list2);
            return list1;
        }
    });
}

And here's your client code.

Collection<Employee> empsWithLowestSalary = employees.stream()
                .collect(minList(Comparator.comparing(Employee::getSalary)));
Ravindra Ranwala
  • 20,744
  • 6
  • 45
  • 63
2

You could group by salary and then retrieve the list of employees with minimum salary:

List<Employee> employees = new ArrayList<Employee>(){{
    add(new Employee("bilbo baggins", 10));
    add(new Employee("frodo baggins", 10));
    add(new Employee("gandalf grey", 100));
}};

Map<Integer, List<Employee>> result = employees.stream().collect(groupingBy(Employee::getSalary));

List<Employee> allMin = result.entrySet().stream()
        .min(Comparator.comparing(Map.Entry::getKey))
        .map(Map.Entry::getValue)
        .orElse(Collections.emptyList());

allMin.forEach(System.out::println);

Output

Employee{name='bilbo baggins', salary=10}
Employee{name='frodo baggins', salary=10}
Dani Mesejo
  • 61,499
  • 6
  • 49
  • 76
2
Integer lowestSalary = employees.stream()
.min(Comparator.comparing(Employee::getSalary))
.map(Employee::getSalary).get();

List<Employee> employeesWithLowestSalary = employees.stream()
.filter(e -> e.getSalary() == lowestSalary)
.collect(Collectors.toList());

Start by finding what the lowest salary is, and then filter the list of employees so that you only have employees whose salary matches.

Katie.Sun
  • 711
  • 3
  • 15
1

You can get the min salary employee first and then filter by it,

  Comparator<Employee> comparator = Comparator.comparing( Employee :: getSalary);
        Optional<Employee> min = employees.stream()
                .min(comparator);
        List<Employee> lowSalary = employees.stream()
                .filter(ele->ele.salary==min.get().salary)
                .collect(Collectors.toList());
Vikas
  • 6,868
  • 4
  • 27
  • 41
1

You can calculate the minimum salary first then use it in your Stream#filtercondition:

int salary = 
   employees.stream()
            .min(Comparator.comparing(Employee::getSalary))
            .map(e -> e.getSalary())
            .orElse(-1);

List<Employee> emps = 
  employees.stream()
           .filter(emp -> emp.getSalary() == salary)
           .collect(Collectors.toList());
Fullstack Guy
  • 16,368
  • 3
  • 29
  • 44