Pick a random value from an enum?
If I have an enum like this:
public enum Letter {
A,
B,
C,
//...
}
What is the best way to pick one randomly? It doesn't need to be production quality bulletproof, but a fairly开发者_高级运维 even distribution would be nice.
I could do something like this
private Letter randomLetter() {
int pick = new Random().nextInt(Letter.values().length);
return Letter.values()[pick];
}
But is there a better way? I feel like this is something that's been solved before.
The only thing I would suggest is caching the result of values()
because each call copies an array. Also, don't create a Random
every time. Keep one. Other than that what you're doing is fine. So:
public enum Letter {
A,
B,
C,
//...
private static final List<Letter> VALUES =
Collections.unmodifiableList(Arrays.asList(values()));
private static final int SIZE = VALUES.size();
private static final Random RANDOM = new Random();
public static Letter randomLetter() {
return VALUES.get(RANDOM.nextInt(SIZE));
}
}
A single method is all you need for all your random enums:
public static <T extends Enum<?>> T randomEnum(Class<T> clazz){
int x = random.nextInt(clazz.getEnumConstants().length);
return clazz.getEnumConstants()[x];
}
Which you'll use:
randomEnum(MyEnum.class);
I also prefer to use SecureRandom as:
private static final SecureRandom random = new SecureRandom();
Single line
return Letter.values()[new Random().nextInt(Letter.values().length)];
Combining the suggestions of cletus and helios,
import java.util.Random;
public class EnumTest {
private enum Season { WINTER, SPRING, SUMMER, FALL }
private static final RandomEnum<Season> r =
new RandomEnum<Season>(Season.class);
public static void main(String[] args) {
System.out.println(r.random());
}
private static class RandomEnum<E extends Enum<E>> {
private static final Random RND = new Random();
private final E[] values;
public RandomEnum(Class<E> token) {
values = token.getEnumConstants();
}
public E random() {
return values[RND.nextInt(values.length)];
}
}
}
Edit: Oops, I forgot the bounded type parameter, <E extends Enum<E>>
.
Simple Kotlin Solution
MyEnum.values().random()
random()
is a default extension function included in base Kotlin on the Collection
object. Kotlin Documentation Link
If you'd like to simplify it with an extension function, try this:
inline fun <reified T : Enum<T>> random(): T = enumValues<T>().random()
// Then call
random<MyEnum>()
To make it static on your enum class. Make sure to import my.package.random
in your enum file
MyEnum.randomValue()
// Add this to your enum class
companion object {
fun randomValue(): MyEnum {
return random()
}
}
If you need to do it from an instance of the enum, try this extension
inline fun <reified T : Enum<T>> T.random() = enumValues<T>().random()
// Then call
MyEnum.VALUE.random() // or myEnumVal.random()
Letter lettre = Letter.values()[(int)(Math.random()*Letter.values().length)];
Agree with Stphen C & helios. Better way to fetch random element from Enum is:
public enum Letter {
A,
B,
C,
//...
private static final Letter[] VALUES = values();
private static final int SIZE = VALUES.length;
private static final Random RANDOM = new Random();
public static Letter getRandomLetter() {
return VALUES[RANDOM.nextInt(SIZE)];
}
}
It's probably easiest to have a function to pick a random value from an array. This is more generic, and is straightforward to call.
<T> T randomValue(T[] values) {
return values[mRandom.nextInt(values.length)];
}
Call like so:
MyEnum value = randomValue(MyEnum.values());
Here a version that uses shuffle and streams
List<Direction> letters = Arrays.asList(Direction.values());
Collections.shuffle(letters);
return letters.stream().findFirst().get();
This is probably the most concise way of achieving your goal.All you need to do is to call Letter.getRandom()
and you will get a random enum letter.
public enum Letter {
A,
B,
C,
//...
public static Letter getRandom() {
return values()[(int) (Math.random() * values().length)];
}
}
If you do this for testing you could use Quickcheck (this is a Java port I've been working on).
import static net.java.quickcheck.generator.PrimitiveGeneratorSamples.*;
TimeUnit anyEnumValue = anyEnumValue(TimeUnit.class); //one value
It supports all primitive types, type composition, collections, different distribution functions, bounds etc. It has support for runners executing multiple values:
import static net.java.quickcheck.generator.PrimitiveGeneratorsIterables.*;
for(TimeUnit timeUnit : someEnumValues(TimeUnit.class)){
//..test multiple values
}
The advantage of Quickcheck is that you can define tests based on a specification where plain TDD works with scenarios.
I guess that this single-line-return method is efficient enough to be used in such a simple job:
public enum Day {
SUNDAY,
MONDAY,
THURSDAY,
WEDNESDAY,
TUESDAY,
FRIDAY;
public static Day getRandom() {
return values()[(int) (Math.random() * values().length)];
}
public static void main(String[] args) {
System.out.println(Day.getRandom());
}
}
It´s eaiser to implement an random function on the enum.
public enum Via {
A, B;
public static Via viaAleatoria(){
Via[] vias = Via.values();
Random generator = new Random();
return vias[generator.nextInt(vias.length)];
}
}
and then you call it from the class you need it like this
public class Guardia{
private Via viaActiva;
public Guardia(){
viaActiva = Via.viaAleatoria();
}
I would use this:
private static Random random = new Random();
public Object getRandomFromEnum(Class<? extends Enum<?>> clazz) {
return clazz.values()[random.nextInt(clazz.values().length)];
}
enum ShapeColor { Blue, Yellow, Red, Green, White, }
Random random=new Random();
ShapeColor[] values=ShapeColor.values();
int size=values.length;
return values[random.nextInt(size)];
public static Letter randomLetter() {
return List.of(values()).get(Random.randomInt(List.of(values()).size()));
}
精彩评论