2009-12-29 44 views
107

Si tengo una enumeración de esta manera:Java: ¿Elige un valor aleatorio de una enumeración?

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

¿Cuál es la mejor manera de elegir uno al azar? No es necesario que sea de calidad de producción a prueba de balas, pero una distribución bastante uniforme sería agradable.

que podía hacer algo como esto

private Letter randomLetter() { 
    int pick = new Random().nextInt(Letter.values().length); 
    return Letter.values()[pick]; 
} 

Pero hay una manera mejor? Siento que esto es algo que ya ha sido resuelto.

+0

lo que crees que es malo en su solución? Se ve bastante bien para mí. –

+1

@GregS: el problema es que cada llamada a 'Letter.values ​​()' tiene que crear una nueva copia de la matriz de valores 'Letter' interna. –

+0

maldición, solo iba a hacer la misma pregunta. nice one (+1) –

Respuesta

104

Lo único que sugeriría es almacenar en caché el resultado de values() porque cada llamada copia una matriz. Además, no cree un Random cada vez. Conserva uno. Aparte de eso, lo que estás haciendo está bien. Por lo tanto:

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)); 
    } 
} 
+7

Si lo considera útil, puede crear una clase de utilidad para hacer esto. Algo como RandomEnum con un constructor que recibe la clase para crear la lista. – helios

+12

Realmente no veo el punto de convertir la matriz 'values ​​()' a una lista no modificable. El objeto 'VALUES' ya está encapsulado en virtud de haber sido declarado' privado'. Sería más simple Y más eficiente convertirla en 'letra estática final privada [] VALORES = ... '. –

+4

Las matrices en Java son mutables, por lo que si tiene un campo de matriz y lo devuelve en un método público, la persona que llama puede modificarlo y modifica el archivo privado, por lo que debe copiar el conjunto de forma defensiva. Si llama a ese método muchas veces, puede ser un problema, por lo que lo coloca en una lista inmutable para evitar una copia defensiva innecesaria. – cletus

37

La combinación de las sugerencias de cletus y 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> { 

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

Editar: Vaya, se me olvidó el parámetro de tipo limitado, <E extends Enum>.

+1

Ver también [* Literales de clase como tokens de tipo de tiempo de ejecución *] (http://docs.oracle.com/javase/tutorial/extra/generics/literals.html). – trashgod

3

Si hace esto para probar, puede usar 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 

Es compatible con todos los tipos primitivos, la composición tipo, colecciones, diferentes funciones de distribución, etc. límites Tiene soporte para los corredores de ejecutar múltiples valores:

import static net.java.quickcheck.generator.PrimitiveGeneratorsIterables.*; 

for(TimeUnit timeUnit : someEnumValues(TimeUnit.class)){ 
    //..test multiple values 
} 

La ventaja de QUICKCHECK es que se puede definir pruebas basadas en specification donde TDD simple funciona con escenarios.

+0

parece intrigante. Tendré que intentarlo. –

+0

Puede enviarme un correo si algo no funciona. Deberías usar la versión 0.5b. –

4
Letter lettre = Letter.values()[(int)(Math.random()*Letter.values().length)]; 
78

Un método sencillo es todo lo que necesita para todas sus enumeraciones al azar:

public static <T extends Enum<?>> T randomEnum(Class<T> clazz){ 
     int x = random.nextInt(clazz.getEnumConstants().length); 
     return clazz.getEnumConstants()[x]; 
    } 

¿Qué que va a utilizar:

randomEnum(MyEnum.class); 

también yo prefiero usar SecureRandom como:

private static final SecureRandom random = new SecureRandom(); 
2

Probablemente sea fácil es tener una función para elegir un valor aleatorio de una matriz. Esto es más genérico y es fácil de llamar.

<T> T randomValue(T[] values) { 
    return values[mRandom.nextInt(values.length)]; 
} 

llamada así:

MyEnum value = randomValue(MyEnum.values()); 
2

Está eaiser para implementar una función aleatoria en la enumeración.

public enum Via { 
    A, B; 

public static Via viaAleatoria(){ 
    Via[] vias = Via.values(); 
    Random generator = new Random(); 
    return vias[generator.nextInt(vias.length)]; 
    } 
} 

y luego te llaman de la clase que lo necesite como esto

public class Guardia{ 
private Via viaActiva; 

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

acuerdo con Stphen C & Helios. Mejor manera para traer elemento aleatorio a partir de enumeración es:

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

sola línea

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

Aquí una versión que utiliza aleatoria y arroyos

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

Esta es probablemente la forma más concisa de lograr su objetivo. Todo lo que necesita hacer es llamar al Letter.getRandom() y obtendrá una carta enum al azar.

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

    public static Letter getRandom() { 
     return values()[(int) (Math.random() * values().length)]; 
    } 
} 
3

me gustaría utilizar esto:

private static Random random = new Random(); 

public Object getRandomFromEnum(Class<? extends Enum<?>> clazz) { 
    return clazz.values()[random.nextInt(clazz.values().length)]; 
} 
Cuestiones relacionadas