Enum से यादृच्छिक मान चुनें?


162

अगर मेरे पास इस तरह की कोई एनम है:

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

बेतरतीब ढंग से एक लेने का सबसे अच्छा तरीका क्या है? यह उत्पादन की गुणवत्ता बुलेटप्रूफ होने की जरूरत नहीं है, लेकिन एक काफी वितरण भी अच्छा होगा।

मैं ऐसा कुछ कर सकता था

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

लेकिन क्या कोई बेहतर तरीका है? मुझे लगता है कि यह कुछ ऐसा है जो पहले हल हो गया है।


आपको क्या लगता है कि आपके समाधान में क्या गलत है? यह मुझे बहुत अच्छा लगता है।
राष्ट्रपति जेम्स के। पोल्क

1
@GregS - समस्या यह है कि प्रत्येक कॉल को Letter.values()आंतरिक Letterमूल्य सरणी की एक नई प्रति तैयार करनी होती है ।
स्टीफन सी

जवाबों:


144

केवल एक चीज जो मैं सुझाऊंगा वह है 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));
  }
}

8
यदि आप इसे उपयोगी मानते हैं, तो आप ऐसा करने के लिए एक उपयोगिता वर्ग बना सकते हैं। रैंडम ईनम <टी जैसा कुछ होता है, लिस्ट बनाने के लिए क्लास <टी> प्राप्त करने वाले कंस्ट्रक्टर के साथ Enum> का विस्तार करता है।
हेलिओस

15
मैं वास्तव में values()सरणी को एक अपरिवर्तनीय सूची में परिवर्तित करने का बिंदु नहीं देखता हूं । VALUESवस्तु पहले से ही घोषित किया गया के आधार पर समझाया गया है private। इसे बनाना सरल और अधिक कुशल होगा private static final Letter[] VALUES = ...
स्टीफन C

4
जावा में एरर्स म्यूट होते हैं इसलिए यदि आपके पास एक ऐरे फ़ील्ड है और इसे एक सार्वजनिक विधि में लौटाते हैं तो कॉलर इसे संशोधित कर सकता है और यह निजी रूप से दायर किए गए को संशोधित करता है ताकि आपको एरे को रक्षात्मक रूप से कॉपी करने की आवश्यकता हो। यदि आप उस विधि को बहुत बार कहते हैं तो यह एक समस्या हो सकती है, इसलिए आप अनावश्यक रक्षात्मक नकल से बचने के लिए इसे अपरिवर्तनीय सूची में डालते हैं।
cletus

1
@ क्लेटस: Enum.values ​​() हर आह्वान पर एक नया सरणी लौटाएगा, इसलिए इसे अन्य स्थानों पर पारित / उपयोग करने से पहले इसे लपेटने की कोई आवश्यकता नहीं है।
Chii

5
निजी स्थिर अंतिम पत्र [] मान्य ... ठीक है। यह निजी है इसलिए यह असंगत है। आपको केवल सार्वजनिक randomLetter () विधि की आवश्यकता है जो स्पष्ट रूप से एक एकल मान लौटाता है। स्टीफन सी सही है।
हेलिओस

126

एक एकल विधि आप सभी अपने सभी यादृच्छिक enums के लिए की जरूरत है:

    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();

1
ठीक वही जो मेरे द्वारा खोजा जा रहा था। मैं स्वीकृत उत्तर की तरह कर रहा था और जब मुझे अपने दूसरे एनम से यादृच्छिक करने के लिए बॉयलरप्लेट कोड के साथ छोड़ दिया गया था। इसके अलावा, कभी-कभी SecureRandom के बारे में भूलना आसान है। धन्यवाद।
सियामास्टर जूल

आपने मेरे दिमाग को पढ़ा, ठीक उसी तरह जो मैं अपनी यादृच्छिक संस्थाओं के जनरेटर टेस्ट क्लास में जोड़ना चाह रहा था। मदद के लिए धन्यवाद
रोके सोसा

43

क्लेटस और हेलिओस के सुझावों को मिलाकर ,

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

संपादित करें: ओह, मैं घिरे प्रकार पैरामीटर, भूल गया <E extends Enum<E>>



1
बहुत पुराना उत्तर मुझे पता है, लेकिन क्या ऐसा नहीं होना चाहिए E extends Enum<E>?
लिनो - वोट

1
@ लिनो: स्पष्टता के लिए संपादित; मुझे नहीं लगता कि यह है के लिए आवश्यक पैरामीटर के लिए बाध्य करने का सही प्रकार निष्कर्ष के लिए, लेकिन मैं स्वागत सुधार चाहते हैं; कृपया ध्यान दें कि new RandomEnum<>(Season.class)जावा 7.
trashgod

यह एकल RandomEnumवर्ग माइक्रो लाइब्रेरी के रूप में अच्छा होगा, अगर आप इसे बंडल करना चाहते हैं और केंद्रीय में प्रकाशित करना चाहते हैं।
ग्रेग चबाला


10

Stphen C & helios से सहमत हैं। 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)];
  }
}


5

यह संभवतः अपने लक्ष्य को प्राप्त करने का सबसे संक्षिप्त तरीका है। आपको कॉल करने की आवश्यकता है Letter.getRandom()और आपको एक यादृच्छिक एनम पत्र मिलेगा।

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

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

5

सरल कोटलिन समाधान

MyEnum.values().random()

random()Collectionऑब्जेक्ट पर बेस कोटलिन में शामिल एक डिफ़ॉल्ट एक्सटेंशन फ़ंक्शन है । कोटलिन डॉक्यूमेंटेशन लिंक

यदि आप इसे एक्सटेंशन फ़ंक्शन के साथ सरल बनाना चाहते हैं, तो यह प्रयास करें:

inline fun <reified T : Enum<T>> random(): T = enumValues<T>().random()

// Then call
random<MyEnum>()

अपने एनम वर्ग पर इसे स्थिर बनाने के लिए। my.package.randomअपनी Enum फ़ाइल में आयात करना सुनिश्चित करें

MyEnum.randomValue()

// Add this to your enum class
companion object {
    fun randomValue(): MyEnum {
        return random()
    }
}

यदि आपको इसे एनम के उदाहरण से करने की आवश्यकता है, तो इस एक्सटेंशन को आज़माएं

inline fun <reified T : Enum<T>> T.random() = enumValues<T>().random()

// Then call
MyEnum.VALUE.random() // or myEnumVal.random() 

4

किसी ऐरे से रैंडम वैल्यू लेने के लिए फंक्शन करना शायद सबसे आसान है। यह अधिक सामान्य है, और कॉल करने के लिए सीधा है।

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

ऐसे करें कॉल:

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

4

यहाँ एक संस्करण है कि फेरबदल और धाराओं का उपयोग करता है

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

3

यदि आप परीक्षण के लिए ऐसा करते हैं तो आप क्विकचेक का उपयोग कर सकते हैं ( यह एक जावा पोर्ट है जिस पर मैं काम कर रहा हूं )।

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
}

क्विकचेक का लाभ यह है कि आप एक विनिर्देश के आधार पर परीक्षणों को परिभाषित कर सकते हैं जहां सादे TDD परिदृश्यों के साथ काम करते हैं।


पेचीदा दिखता है। मुझे इसे आजमाना होगा।
निक हेइनर

अगर कुछ काम नहीं आया तो आप मुझे मेल कर सकते हैं। आपको 0.5b संस्करण का उपयोग करना चाहिए।
थॉमस जुंग

2

यह ईरम पर एक यादृच्छिक समारोह को लागू करने के लिए है।

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


1

मुझे लगता है कि यह एकल-पंक्ति-वापसी विधि इतनी सरल नौकरी में उपयोग करने के लिए पर्याप्त है:

public enum Day {
    SUNDAY,
    MONDAY,
    THURSDAY,
    WEDNESDAY,
    TUESDAY,
    FRIDAY;

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

    public static void main(String[] args) {
        System.out.println(Day.getRandom());
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.