web-dev-qa-db-fra.com

Choisir une valeur aléatoire dans une énumération?

Si j'ai un enum comme ça:

public enum Letter {
    A,
    B,
    C,
    //...
}

Quelle est la meilleure façon de choisir un au hasard? Il n’est pas nécessaire que la qualité de production soit optimale, mais une distribution assez égale serait Nice.

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 une meilleure façon? J'ai l'impression que c'est quelque chose qui a été résolu avant.

142
Nick Heiner

La seule chose que je suggérerais est de mettre en cache le résultat de values() parce que chaque appel copie un tableau. Aussi, ne créez pas un Random à chaque fois. Gardez-en un. Autre que ce que vous faites est bien. Alors:

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));
  }
}
129
cletus

Une seule méthode suffit pour tous 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 en tant que:

private static final SecureRandom random = new SecureRandom();
106
Eldelshell

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>>.

42
trashgod

Une seule ligne

return Letter.values()[new Random().nextInt(Letter.values().length)];
25

D'accord avec Stphen C & helios. Le meilleur moyen de récupérer un élément aléatoire 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)];
  }
}
10
Deepti

Voici une version qui utilise shuffle et stream

List<Direction> letters = Arrays.asList(Direction.values());
Collections.shuffle(letters);
return letters.stream().findFirst().get();
4
major seitan

C’est probablement le moyen le plus concis d’atteindre votre objectif. Il vous suffit d’appeler Letter.getRandom() pour obtenir une lettre au hasard.

public enum Letter {
    A,
    B,
    C,
    //...

    public static Letter getRandom() {
        return values()[(int) (Math.random() * values().length)];
    }
}
4
Adilli Adil
Letter lettre = Letter.values()[(int)(Math.random()*Letter.values().length)];
4
anonymous

Il est probablement plus facile d'avoir une fonction qui sélectionne 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)];
}

Appelle comme ça:

MyEnum value = randomValue(MyEnum.values());
3
Joseph Thomson

Si vous faites cela pour les tests, vous pouvez utiliser Quickcheck ( c'est un Java le port sur lequel j'ai travaillé ).

import static net.Java.quickcheck.generator.PrimitiveGeneratorSamples.*;

TimeUnit anyEnumValue = anyEnumValue(TimeUnit.class); //one value

Il supporte tous les types primitifs, composition de type, collections, différentes fonctions de distribution, bornes, 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 un spécification où TDD simple fonctionne avec des scénarios.

3
Thomas Jung

Il est plus facile d'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 ça

public class Guardia{
private Via viaActiva;

public Guardia(){
    viaActiva = Via.viaAleatoria();
}
2
Folea

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)];
}
2
user5910225