12

Following this question about sorting a list by another list, I tried to do the same thing - but from some reason it doesn't work for me. What am I missing?

    List<Double> nums = Arrays.asList(5.0, 0.9, 10.4);
    List<Double> order = Arrays.asList(3.0, 1.0, 2.0);
    nums.sort(Comparator.comparing(order::indexOf));
    System.out.println(nums);

    OUTPUT: [5.0, 0.9, 10.4]

It should be [0.9, 10.4, 5.0] (according to order). What am I not doing right?

EDIT: As most of you noticed, I got answer to the question I linked to all wrong. Here's what I actually want to do.

shakedzy
  • 2,853
  • 5
  • 32
  • 62

4 Answers4

16

You are sorting the numbers by their position in the order list, but none of the numbers occur in the order list. In this case, indexOf will return -1 for everything, meaning everything is equal to everything else. In such a case, the resulting sort order is unspecified - though you may realistically assume that it would not change.

Michael
  • 41,989
  • 11
  • 82
  • 128
  • Oh, so I got it all wrong.. how then do I sort `nums` by `order`? – shakedzy May 16 '19 at 09:56
  • @Michael I believe using `order` as the keys for sorting. – justhalf May 16 '19 at 14:55
  • @shakedzy I'd like to add that the comparator does not fulfil the `Comparator` [specification](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#compare-T-T-). That implies that not only is the sort order unspecified, but sort methods might throw exception. `ArrayList` for example [might throw](https://hg.openjdk.java.net/jdk/jdk/file/830ca7b43b95/src/java.base/share/classes/java/util/TimSort.java#l288) `AssertionError`. – Lii Jun 06 '19 at 16:13
  • Just for fun I played with `ArrayList` and different flawed comparators. I never get an exception with comparators that always return -1, and I never get `AssertionError`s. With comparators that return random integers I often get [`IllegalArgumentException`](https://hg.openjdk.java.net/jdk/jdk/file/830ca7b43b95/src/java.base/share/classes/java/util/TimSort.java#l781) however. (Of course, all this is unspecified and implementation dependent, so it might vary between JRE:s and releases.) – Lii Jun 07 '19 at 08:49
4

You can make a list of pairs :

[3.0, 5.0]
[1.0, 0.9]
[2.0, 10.4]

Then sort this list of pairs by the first value of each array :

[1.0, 0.9]
[2.0, 10.4]
[3.0, 5.0]

Here is the code :

List<Double> nums = Arrays.asList(5.0, 0.9, 10.4);
List<Double> order = Arrays.asList(3.0, 1.0, 2.0);

List<Double[]> pairs = new ArrayList<>();
for (int i = 0; i < nums.size(); i++) {
    pairs.add(new Double[] {order.get(i), nums.get(i)});
}

pairs.sort(Comparator.comparing(pair -> pair[0]));

for (Double[] pair : pairs) {
    System.out.print(pair[1] + " ");
}

Output :

0.9 10.4 5.0 
Arnaud Denoyelle
  • 29,980
  • 16
  • 92
  • 148
2

Update

List<Double> nums = Arrays.asList(5.0, 0.9, 10.4);
List<Double> order = Arrays.asList(3.0, 1.0, 2.0);
Map<Double,Double> numToOrder = new HashMap<>();
for (int i = 0; i < nums.size(); ++i) {
    numToOrder.put(nums.get(i), order.get(i));
}
nums.sort(Comparator.comparing(num -> numToOrder.get(num)));
System.out.println(nums);

Original (wrong) answer

(nums is modified in place, and the lambda returning key returns wrong results)

List<Double> nums = Arrays.asList(5.0, 0.9, 10.4);
List<Double> order = Arrays.asList(3.0, 1.0, 2.0);
nums.sort(Comparator.comparing(num -> order.get(nums.indexOf(num))));
System.out.println(nums);
Lesiak
  • 22,088
  • 2
  • 41
  • 65
1

The Comparator you are supplying calls indexOf for every num passed. The returned values are -1 on all calls, so the order is preserverd as-is.

You need to sort natural.

Sorting by another list of Double should be possible, but unnecessarily complicated, it would be simpler to provide a custom Object which sorts as desired.