2009-12-29 171 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

你觉得有什么不对您的解决方案?它对我来说很不错。 – 2009-12-29 00:57:59

+1

@GregS - 问题是每次调用'Letter.values()'都必须创建一个内部的'Letter'值数组的新副本。 – 2009-12-29 02:13:44

+0

darn,我只是想问同样的问题。 (+1) – 2011-01-04 12:12:12

回答

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 2009-12-29 01:16:32

+12

我真的没有看到将'values()'数组转换为不可修改的列表。 'VALUES'对象由于被声明为'private'已经被封装了。这会更简单,更高效地使它成为'private static final Letter [] VALUES = ...'。 – 2009-12-29 01:56:15

+4

Java中的数组是可变的,所以如果你有一个数组字段并在公共方法中返回它,调用者可以修改它,它修改私有字段,所以你需要防御地复制数组。如果您多次调用该方法,可能会出现问题,因此您需要将其置于不可变列表中,以避免不必要的防御性复制。 – cletus 2009-12-29 02:48:53

37

结合的建议的cletushelios

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

编辑:哎呀,我忘了有限制类型参数,<E extends Enum>

+1

另请参见[*文字作为运行时类型标记*](http://docs.oracle.com/javase/tutorial/extra/generics/literals.html)。 – trashgod 2013-07-06 13:01:45

3

如果你这样做了测试,你可以使用Quickcheckthis 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 
} 

快速检查的优点是,可以定义基于specification的测试,其中普通TDD适用于场景。

+0

看起来很有趣。我必须试一试。 – 2009-12-29 18:18:45

+0

如果出现问题,您可以发邮件给我。你应该使用0.5b版本。 – 2009-12-29 19:48:28

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

这可能很简单est有一个函数可以从数组中选取一个随机值。这是更通用的,并且很简单。

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

呼叫,像这样:

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

It's 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Ç&赫利俄斯同意。更好地从枚举获取随机元素的方法是:

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