-1

I want to shuffle a list. The way I want to do it is using sort method on list of java 8. I want to assign a random number between 0 to 1 to each number and based on that to sort the list. Here what I have and it works:

List<Integer> list = Arrays.asList(1,2,3,4,5);
list.sort(comparing(x->new Random().nextFloat()));

and it works. But when I want to use reference method like that:

list.sort(comparing(Random::nextFloat));

I get compile time error and I dont understand why. After all my comparator generating random number for each element and then sorts by this random number. Any ideas?

Thank u in advance

Alexis C.
  • 91,686
  • 21
  • 171
  • 177
user1409534
  • 2,140
  • 4
  • 27
  • 33

1 Answers1

5

The comparing() method expects you to pass it a Function that takes an Integer and that returns a sort key, which is a Float (or float) in this case.

The nextFloat() method in class Random does not take an Integer (or int) argument, so a method reference to Random::nextFloat will not work - the method does not have the expected signature.

You could make a helper method:

public class Example {
    private final Random rnd = new Random();

    private float nextFloat(int dummy) {
        return rnd.nextFloat();
    }

    public void shuffle() {
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        list.sort(comparing(this::nextFloat));
    }
}

But there's an even bigger problem. This solution is not doing what you think it is doing:

I want to assign a random number between 0 to 1 to each number and based on that to sort the list.

Instead of assigning a random number to each number in the list and sort based on that, you are returning a different random number every time the comparison method is called. The sort algorithm that you are using might be calling the comparison method multiple times for the same integer. In that case you are returning a different random number each time.

This might lead to very strange problems, depending exactly on what sorting algorithm you use. It might even never terminate.

Implementing this correctly is considerably more complicated than you think. If this is for a real application and not just for practice, use Collections.shuffle() instead of implementing this yourself.

Here is a working version:

import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.Random;
import java.util.stream.Collectors;

import static java.util.Comparator.comparing;

public final class ShuffleExample {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

        // Create a list of Entry objects, which are pairs of a random float
        // and a value from the list
        Random rnd = new Random();
        List<Entry<Float, Integer>> entries = new ArrayList<>();
        for (Integer value : list) {
            entries.add(new SimpleEntry<>(rnd.nextFloat(), value));
        }

        // Sort the list of Entry objects by the floats
        entries.sort(comparing(Entry::getKey));

        // Get the int values out of the sorted list
        List<Integer> shuffled = entries.stream()
                .map(Entry::getValue)
                .collect(Collectors.toList());

        System.out.println(shuffled);
    }
}
Jesper
  • 202,709
  • 46
  • 318
  • 350
  • Wouldn't that require a method `nextFloat(int)`? – Bubletan Mar 28 '15 at 21:18
  • I don't understand why the sort might be calling the comparison method multiple times for the same integer ? – user1409534 Mar 29 '15 at 08:49
  • @user1409534 It depends on the sorting algorithm. There are many different sorting algorithms, and many of them need to compare an element with multiple other elements. There is no guarantee that the comparison function will be called only once for each element. – Jesper Mar 29 '15 at 11:04
  • Since it's java 8 you could do `List> entries = list.stream().map(i -> new SimpleEntry<>(rnd.nextFloat(), i)).collect(toList());` instead of the for-each. Also note that there are comparators defined for Map entries, so you could also do `entries.sort(comparingByKey());` (with a static import `java.util.Map.Entry.comparingByKey;`) – Alexis C. Mar 29 '15 at 13:50