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 Integer
s, 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.
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With