2009-12-29 4 views
107

Если у меня есть перечисление вроде этого:Java: выберите случайное значение из перечисления?

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

Каков наилучший способ выбрать один случайно? Это не должно быть качество продукции пуленепробиваемое, но довольно ровное распределение было бы неплохо.

я мог бы сделать что-то вроде этого

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

Но есть лучший путь? Я чувствую, что это то, что было решено раньше.

+0

что вы думаете, не так с вашим решением? Это выглядит очень хорошо для меня. –

+1

@GregS - проблема в том, что каждый вызов 'Letter.values ​​()' должен создать новую копию внутреннего массива значений 'Letter'. –

+0

Дарн, я просто хотел задать тот же вопрос. nice one (+1) –

ответ

104

Единственное, что я хотел бы предложить, это кеширование результата values(), потому что каждый вызов копирует массив. Кроме того, не создавайте Random каждый раз. Держи один. Кроме того, что вы делаете, все в порядке. Итак:

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

Если вы считаете это полезным, вы можете сделать для него полезный класс. Что-то вроде RandomEnum с конструктором, получающим класс для создания списка. – helios

+12

Я действительно не вижу смысла в преобразовании массива 'values ​​()' в немодифицируемый список. Объект 'VALUES' уже инкапсулирован в силу объявления' private'. Было бы проще и эффективнее сделать это 'private static final Letter [] VALUES = ...'. –

+4

Массивы в Java изменяемы, поэтому, если у вас есть поле массива и оно возвращается в открытый метод, вызывающий может его модифицировать, и он изменяет приватную подачу, поэтому вам необходимо защитить копию массива. Если вы вызываете этот метод много раз, это может быть проблемой, поэтому вместо этого вы помещаете его в неизменный список, чтобы избежать ненужного оборонительного копирования. – cletus

37

Объединяя предложения о cletus и 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)]; 
     } 
    } 
} 

Edit: К сожалению, я забыл параметр ограниченного типа, <E extends Enum>.

+1

См. Также [* Литералы классов в качестве маркеров типа времени выполнения] (http://docs.oracle.com/javase/tutorial/extra/generics/literals.html). – trashgod

3

Если вы сделаете это для тестирования, вы можете использовать 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 

Он поддерживает все примитивные типы, состав типа, коллекции, различные функции распределения, границы и т.д. Он имеет поддержку бегунов выполнять множество значений:

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

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

Преимущество QuickCheck в том, что вы можете определить тесты основаны на specification, где обычный TDD работает со сценариями.

+0

выглядит интригующим. Я должен попробовать. –

+0

Вы можете выслать мне письмо, если что-то не работает. Вы должны использовать версию 0.5b. –

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

Один метод все, что нужно для всех ваших случайных перечислений:

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

Что вы будете использовать:

randomEnum(MyEnum.class); 

Я также предпочитаю использовать SecureRandom как:

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

Это, вероятно, easi чтобы иметь функцию выбора случайного значения из массива. Это более общий характер, и его просто позвонить.

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

вызовов следующим образом:

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

подмигнули eaiser для реализации случайной функции на перечислении.

public enum Via { 
    A, B; 

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

, а затем вы называете его из класса вам это нужно, как этот

public class Guardia{ 
private Via viaActiva; 

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

Согласен с Stphen C & Гелиоса. Лучший способ извлечь случайный элемент из Enum является:

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

Однолинейная

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

Вот версия, которая использует перетасовать и потоки

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

Это, вероятно, самый краткий способ достижения ваша цель. Все, что вам нужно сделать, это позвонить Letter.getRandom(), и вы получите случайное письмо с перечислением.

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

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

Я хотел бы использовать это:

private static Random random = new Random(); 

public Object getRandomFromEnum(Class<? extends Enum<?>> clazz) { 
    return clazz.values()[random.nextInt(clazz.values().length)]; 
} 
Смежные вопросы