Sort a list of numbers with all 0's at the end of result

Viewed 763

I have the list of persons and if the list of Input is 3,0,2,7,0,8

I want the output to be 2,3,7,8,0,0

With the following code, i can sort only non zero numbers to get 2,3,7,8 as output.

sortedList = personList.stream()
                       .filter(Person -> Person.getAge()>0)
                       .sorted(Comparator.comparingInt(Person::getAge))
                       .collect(Collectors.toList());

zeroList=personList.stream()
                       .filter(Person -> Person.getAge()==0)
                       .collect(Collectors.toList());

sortedList.addAll(zeroList);

Can the above two statements be merged into a single statement?

5 Answers

You can make sure that zeros go to the end by writing an appropriate comparator:

sortedList = personList.stream()
   .sorted((p1, p2) -> {
      int a = p1.getAge();
      int b = p2.getAge();
      if (a == 0 && b == 0) return 0;
      if (a == 0) return 1;
      if (b == 0) return -1;
      return Integer.compare(a, b);
   })
   .collect(Collectors.toList());

Or slightly more compactly:

sortedList = personList.stream()
   .sorted((p1, p2) -> {
      int a = p1.getAge();
      int b = p2.getAge();
      return a == 0 || b == 0 ? Integer.compare(b, a) : Integer.compare(a, b);
   })
   .collect(Collectors.toList());

A nice simple way to put 0s at the end of the sort is to treat them as large values in the comparator:

Comparator.comparingInt(p -> p.getAge() == 0 ? Integer.MAX_VALUE : p.getAge())

You may concatenate the two stream before building the list:

sortedList = Stream<Person>.concat(
   personList.stream().filter(Person -> Person.getAge()>0)
                      .sorted(Comparator.comparingInt(Person::getAge)),
   personList.stream().filter(Person -> Person.getAge()==0))
                      .collect(Collectors.toList());

What you want, is a rotation; when you simply sort by age, the elements are in the right order, when you consider the result list to be a ring, so you only want to start the list after the last zero-age value:

┌───────────────────────────┐
└─ 0  0  2  3  7  8       <─┘
         2  3  7  8  0  0 
sortedList = personList.stream()
    .sorted(Comparator.comparingInt(Person::getAge))
    .collect(Collectors.toCollection(ArrayList::new));
Collections.rotate(sortedList, -(int)personList.stream().filter(p->p.getAge()==0).count());

Alternatively, you can rotate the age values within the int value range while comparing. This works, because we can preclude negative ages, so we can rotate the numbers so that zero becomes the MAX_VALUE while all other numbers keep their relative order.

sortedList = personList.stream()
    .sorted(Comparator.comparingInt(p -> p.getAge()-Integer.MIN_VALUE-1))
    .collect(Collectors.toList());

By the way, this is equivalent to

sortedList = personList.stream()
    .sorted((p1, p2) -> Integer.compareUnsigned(p1.getAge()-1, p2.getAge()-1))
    .collect(Collectors.toList());

Understanding the two's complement is needed to understand the solution.

You specified a filter .filter(Person -> Person.getAge()>0) that does not allow element `equals to 0

You need to change to : .filter(Person -> Person.getAge()>=0)

Then you need to specify a more complex comparator

sortedlist = personList.stream()
            .filter(Person -> Person.getAge() >= 0)
            .sorted((o1, o2) -> {
                if (o1.getAge() == 0 || o2.getAge() == 0) 
                    return Integer.compare(o2.getAge(), o1.getAge());
                return Integer.compare(o1.getAge(), o2.getAge());
            })
            .collect(Collectors.toList());
  • If one of the Person's age is 0 it'll compare in reverse way (0 will be the bigger numer => at the end)
  • if none of ages is 0 : classic comparison between Integers
Related