4

Given a class.

public class Entity {

    private Long id;
    private String prodName;
    private BigDecimal price;

    // Constructors + getters + setters + hashcode() + equals() + toString().
}

Constructing a list of Entity.

List<Entity> list = new ArrayList<>();

Entity entity = new Entity();
entity.setId(1L);
entity.setProdName("A");
entity.setPrice(new BigDecimal(10));
list.add(entity);

entity = new Entity();
entity.setId(2L);
entity.setProdName("B");
entity.setPrice(new BigDecimal(20));
list.add(entity);

entity = new Entity();
entity.setId(3L);
entity.setProdName("C");
entity.setPrice(new BigDecimal(30));
list.add(entity);

entity = new Entity();
entity.setId(4L);
entity.setProdName("D");
entity.setPrice(new BigDecimal(40));
list.add(entity);

entity = new Entity();
entity.setId(5L);
entity.setProdName("E");
entity.setPrice(new BigDecimal(50));
list.add(entity);

entity = new Entity();
entity.setId(6L);
entity.setProdName("F");
entity.setPrice(new BigDecimal(60));
list.add(entity);

entity = new Entity();
entity.setId(7L);
entity.setProdName("F");
entity.setPrice(new BigDecimal(60));
list.add(entity);

Attempting to sort the list in descending order by price and prodName and then in ascending order by id.

Comparator<Entity> comparator = Comparator.comparing(Entity::getPrice).reversed();
comparator = comparator.thenComparing(Entity::getProdName).reversed();
comparator = comparator.thenComparingLong(Entity::getId);

list = list.stream().sorted(comparator).collect(Collectors.toList());
// Or Collections.sort(list, comparator);
list.stream().forEachOrdered(l -> System.out.println(l.getId() + " : " + l.getPrice() + " : " + l.getProdName()));

After performing sorting, the list should appear like the following.

6 : 60 : F
7 : 60 : F
5 : 50 : E
4 : 40 : D
3 : 30 : C
2 : 20 : B
1 : 10 : A

But the list, after sorting is performed, appears as follows.

1 : 10 : A
2 : 20 : B
3 : 30 : C
4 : 40 : D
5 : 50 : E
6 : 60 : F
7 : 60 : F

The sorted list is not according to the given criteria (descending order by price and prodName and ascending order by id).

What more needs to be done?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
Tiny
  • 27,221
  • 105
  • 339
  • 599
  • Try some code based on the examples provided in this article: http://www.leveluplunch.com/java/tutorials/007-sort-arraylist-stream-of-objects-in-java8/ – Dominik Sandjaja Oct 08 '15 at 12:06
  • `comparator.thenComparingLong(Entity::getId).reversed()`? – marstran Oct 08 '15 at 12:07
  • [Here](http://stackoverflow.com/questions/14154127/collections-sortlistt-comparator-super-t-method-example) is a good example for collection sort using Comparator. – mystery Oct 08 '15 at 12:08
  • You could leave the sorting like it is and use the static call to `Collections.reverse(list)` afterwards – Westranger Oct 08 '15 at 12:09
  • just get rid of `reveresed()` from your comparator... – nafas Oct 08 '15 at 12:10

2 Answers2

8

You're reversing the whole comparator every time you call reversed(). Instead, just do as your description says:

Comparator<Entity> comparator = Comparator.comparing(Entity::getPrice,
                                                     Comparator.reverseOrder());
comparator = comparator.thenComparing(Entity::getProdName, 
                                      Comparator.reverseOrder());
comparator = comparator.thenComparingLong(Entity::getId);

list = list.stream().sorted(comparator).collect(Collectors.toList());
list.stream().forEachOrdered(l -> System.out.println(l.getId() + " : " + l.getPrice() + " : " + l.getProdName()));
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 4
    Of course, programmers who realized that `reversed()` applies to the whole comparator may simply use `Comparator.comparing(Entity::getPrice).thenComparing(Entity::getProdName).reversed() .thenComparingLong(Entity::getId)` to achieve the same… – Holger Oct 08 '15 at 15:28
5

Consider your code commented:

Comparator<Entity> comparator = Comparator.comparing(Entity::getPrice).reversed(); // compare by descending price
comparator = comparator.thenComparing(Entity::getProdName) // then compare by ascending name
                       .reversed(); // then reverse this order: so you now sort by ascending price and descending name
comparator = comparator.thenComparingLong(Entity::getId); // and finally compare by ascending id

So the final comparator is: ascending price, descending name then ascending ID, which is exactly what you have as result.

So, the mistake is that you are chaining incorrectly the calls to reversed().

The following will work:

Comparator<Entity> comparator = Comparator.comparing(Entity::getPrice).reversed(); //sort by descending price
comparator = comparator.thenComparing(Comparator.comparing(Entity::getProdName).reversed()); // then sort by descending name
comparator = comparator.thenComparingLong(Entity::getId); // then sort by ascending id
Tunaki
  • 132,869
  • 46
  • 340
  • 423