Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Collections#shuffle not use ThreadLocalRandom?

public static void shuffle(List<?> list) {
    Random rnd = r;
    if (rnd == null)
        r = rnd = new Random(); // harmless race.
    shuffle(list, rnd);
}

private static Random r;

I noticed that Collections#shuffle uses new Random() instead of ThreadLocalRandom.current(), and I'm curious as to why. I wrote a benchmark comparing the two, and the method that uses ThreadLocalRandom.current() is faster for each iteration. Here's the benchmark:

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 15, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 20, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Fork(5)
public class MyBenchmark {
    @Param({"10", "1000", "100000"})
    private int i;

    private List<Integer> list;

    private static final Random RANDOM = new Random();

    @Setup
    public void initialize() {
        list = ThreadLocalRandom.current()
                                .ints(i)
                                .boxed()
                                .collect(Collectors.toList());
    }

    public static void main(String[] args) throws Exception {
        org.openjdk.jmh.Main.main(args);
    }

    @Benchmark
    public List<Integer> oldMethod() {
        Collections.shuffle(list, RANDOM);
        return list;
    }

    @Benchmark
    public List<Integer> newMethod() {
        Collections.shuffle(list, ThreadLocalRandom.current());
        return list;
    }
}

And the results:

Benchmark                 (i)  Mode  Cnt        Score       Error  Units
MyBenchmark.newMethod      10  avgt  100       60.802 ±     0.785  ns/op
MyBenchmark.newMethod    1000  avgt  100     6879.496 ±    15.638  ns/op
MyBenchmark.newMethod  100000  avgt  100  1248858.889 ± 33632.559  ns/op
MyBenchmark.oldMethod      10  avgt  100       90.636 ±     1.379  ns/op
MyBenchmark.oldMethod    1000  avgt  100     9740.442 ±    57.373  ns/op
MyBenchmark.oldMethod  100000  avgt  100  1511428.034 ± 27744.775  ns/op

So, why is new Random() used over ThreadLocalRandom.current()?

My guesses:

  • This method has not been updated since the release of ThreadLocalRandom in JDK 1.7
  • new Random() is used for thread-safety?
like image 576
Jacob G. Avatar asked Oct 17 '22 17:10

Jacob G.


1 Answers

The Collections.shuffle(List) and Collections.shuffle(List, Random) methods were added in JDK 1.2, whereas the ThreadLocalRandom class wasn't added until Java 7. Thus, for all the releases prior to Java 7, shuffle(List) used an instance of Random.

Although the source of randomness for shuffle(List) isn't specified, it would have possibly have been an unexpected change in behavior for it to be changed to use a different source of randomness. Thus, the method has been left as it stands, using an instance of Random.

There's no great need to change it. Other sources of randomness, such as ThreadLocalRandom and SecureRandom, are subclasses of Random, and they can be used for shuffling using the two-arg overload shuffle(List, Random) if that's what the application needs.

like image 187
Stuart Marks Avatar answered Oct 20 '22 23:10

Stuart Marks