2009-12-29 172 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)]; 
}