Choisissez une valeur aléatoire à partir d'une énumération?
Si j'ai une énumération comme ceci:
public enum Letter {
A,
B,
C,
//...
}
Quelle est la meilleure façon d'en choisir une au hasard? Il n'a pas besoin d'être de qualité de production à l'épreuve des balles, mais une distribution assez uniforme serait bien.
Je pourrais faire quelque chose comme ça
private Letter randomLetter() {
int pick = new Random().nextInt(Letter.values().length);
return Letter.values()[pick];
}
Mais y a-t-il un meilleur moyen? J'ai l'impression que c'est quelque chose qui a été résolu avant.
12 réponses
La seule chose que je suggère est de mettre en cache le résultat de values()
car chaque appel copie un tableau. De plus, ne créez pas de Random
à chaque fois. Gardez-en un. À part ça, ce que tu fais va bien. Donc:
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));
}
}
Une seule méthode est tout ce dont vous avez besoin pour toutes vos énumérations aléatoires:
public static <T extends Enum<?>> T randomEnum(Class<T> clazz){
int x = random.nextInt(clazz.getEnumConstants().length);
return clazz.getEnumConstants()[x];
}
Que vous utiliserez:
randomEnum(MyEnum.class);
Je préfère aussi utiliser SecureRandom comme:
private static final SecureRandom random = new SecureRandom();
Combinant les suggestions de cletus et 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: Oups, j'ai oublié le paramètre de type borné, <E extends Enum<E>>
.
Ligne unique
return Letter.values()[new Random().nextInt(Letter.values().length)];
D'accord avec Stphen C & helios. La meilleure façon de récupérer un élément aléatoire à partir D'Enum est:
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)];
}
}
Letter lettre = Letter.values()[(int)(Math.random()*Letter.values().length)];
Si vous faites cela pour les tests, vous pouvez utiliser Quickcheck (c'est un port Java sur lequel j'ai travaillé).
import static net.java.quickcheck.generator.PrimitiveGeneratorSamples.*;
TimeUnit anyEnumValue = anyEnumValue(TimeUnit.class); //one value
Il prend en charge tous les types primitifs, la composition de type, les collections, les différentes fonctions de distribution,les limites, etc. Il prend en charge les coureurs exécutant plusieurs valeurs:
import static net.java.quickcheck.generator.PrimitiveGeneratorsIterables.*;
for(TimeUnit timeUnit : someEnumValues(TimeUnit.class)){
//..test multiple values
}
L'avantage de Quickcheck est que vous pouvez définir des tests basés sur une spécification où plain TDD fonctionne avec des scénarios.
Il est probablement plus facile d'avoir une fonction pour choisir une valeur aléatoire dans un tableau. Ceci est plus générique, et est simple à appeler.
<T> T randomValue(T[] values) {
return values[mRandom.nextInt(values.length)];
}
Appelez comme ceci:
MyEnum value = randomValue(MyEnum.values());
Voici une version qui utilise shuffle et streams
List<Direction> letters = Arrays.asList(Direction.values());
Collections.shuffle(letters);
return letters.stream().findFirst().get();
C'est probablement la façon la plus concise d'atteindre votre objectif.Tout ce que vous devez faire est d'appeler Letter.getRandom()
et vous obtiendrez un hasard enum lettre.
public enum Letter {
A,
B,
C,
//...
public static Letter getRandom() {
return values()[(int) (Math.random() * values().length)];
}
}
Son eaiser pour implémenter une fonction aléatoire sur l'énumération.
public enum Via {
A, B;
public static Via viaAleatoria(){
Via[] vias = Via.values();
Random generator = new Random();
return vias[generator.nextInt(vias.length)];
}
}
Et puis vous l'appelez de la classe dont vous avez besoin comme ceci
public class Guardia{
private Via viaActiva;
public Guardia(){
viaActiva = Via.viaAleatoria();
}
Je voudrais utiliser ceci:
private static Random random = new Random();
public Object getRandomFromEnum(Class<? extends Enum<?>> clazz) {
return clazz.values()[random.nextInt(clazz.values().length)];
}