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.

120
demandé sur Lii 2009-12-29 03:54:29

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));
  }
}
119
répondu cletus 2009-12-29 01:02:34

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();
87
répondu Eldelshell 2017-02-21 16:02:32

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

40
répondu trashgod 2018-08-14 16:18:59

Ligne unique

return Letter.values()[new Random().nextInt(Letter.values().length)];
17
répondu M-T-A 2015-06-04 11:17:18

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)];
  }
}
8
répondu Deepti 2015-04-23 08:55:48
Letter lettre = Letter.values()[(int)(Math.random()*Letter.values().length)];
4
répondu anonymous 2017-09-19 10:04:27

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.

3
répondu Thomas Jung 2009-12-29 09:45:06

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());
3
répondu Joseph Thomson 2013-11-18 11:34:50

Voici une version qui utilise shuffle et streams

List<Direction> letters = Arrays.asList(Direction.values());
Collections.shuffle(letters);
return letters.stream().findFirst().get();
3
répondu major seitan 2015-07-22 20:06:59

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)];
    }
}
3
répondu Adilli Adil 2015-12-19 08:25:13

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();
}
2
répondu Folea 2014-06-16 21:09:50

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
répondu Konstantin Pavlov 2016-08-28 11:34:48