Neuron
Neuron

Reputation: 5851

Random permutation of IntStream

Every now and then I find myself with indexed loops, for which I want to permutate the order to some random order. I usually transition from something like

for (int i = 0; i < max; i++) {
    // do stuff with i
}

to

List<Integer> indices = IntStream.range(0, max)
    .boxed()
    toCollection(() -> new ArrayList(max)));
Collections.shuffle(indices);
for (int i = 0; i < max; i++) {
    int index = indices.get(i);
    // do stuff with index
}

This is neither efficient nor elegant. Is it possible to create a Stream (ideally an IntStream) in a certain range, but have it return its elements shuffled? I am thinking of something along the lines of:

IntStream.range(0, max)
        .shuffled() // this method doesn't exist
        .forEach(IntConsumer::accept);

The resulting IntStream should still include all elements in the range [0, max) exactly once.


This is not a duplicate of this question, because I don't want to create a List and shuffle it. This solution has a massive overhead since it is working with Integers, while also redundantly creating and shuffling a List. I have provided that solution in my own example, so I am fully aware of that approach.

Upvotes: 4

Views: 341

Answers (2)

Thomas Francois
Thomas Francois

Reputation: 866

You could use Random.ints:

new Random().ints(0, max)
    .distinct()
    .limit(max)
    .forEach(IntConsumer::accept);

ints will produce a stream of ints between 0 and max, distinct makes sure that there are no duplicates, and limit to get exactly how many you want.

Upvotes: 1

Willis Blackburn
Willis Blackburn

Reputation: 8204

How about this? It's pretty much the same thing you have, except it encapsulates all the nitty gritty and just gives you a pure IntStream. Also it doesn't have to do so much boxing and unboxing.

public class ShuffledIntStream {

    public static IntStream to(int max) {
        Random r = new Random();
        int[] values = new int[max];
        for (int i = 0; i < max; i++) {
            values[i] = i;
        }
        for (int i = max; i > 1; i--) {
            swap(values, i - 1, r.nextInt(max));
        }
        return IntStream.of(values);
    }

    private static void swap(int[] values, int i, int j) {
        int temp = values[i];
        values[i] = values[j];
        values[j] = temp;
    }
}

Upvotes: 5

Related Questions