Chọn một giá trị ngẫu nhiên từ một enum?


161

Nếu tôi có một enum như thế này:

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

Cách tốt nhất để chọn ngẫu nhiên là gì? Nó không cần phải có chất lượng chống đạn, nhưng phân phối khá đồng đều sẽ tốt.

Tôi có thể làm một cái gì đó như thế này

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

Nhưng có cách nào tốt hơn không? Tôi cảm thấy như đây là điều đã được giải quyết trước đây.


Bạn nghĩ gì là sai với giải pháp của bạn? Nó trông khá tốt với tôi.
Tổng thống James K. Polk

1
@GregS - vấn đề là mỗi cuộc gọi Letter.values()phải tạo một bản sao mới của Lettermảng giá trị nội bộ .
Stephen C

Câu trả lời:


143

Điều duy nhất tôi muốn đề xuất là lưu trữ kết quả values()vì mỗi cuộc gọi sao chép một mảng. Ngoài ra, đừng tạo ra Randommọi lúc. Giư một. Khác với những gì bạn đang làm là tốt. Vì thế:

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
Nếu bạn thấy nó hữu ích, bạn có thể tạo một lớp tiện ích để làm việc này. Một cái gì đó như RandomEnum <T mở rộng Enum> với một hàm tạo nhận Class <T> để tạo danh sách.
helios

15
Tôi thực sự không thấy điểm chuyển đổi values()mảng thành một danh sách không thể thay đổi. Đối VALUEStượng đã được gói gọn nhờ đức tính được tuyên bố private. Nó sẽ đơn giản hơn và hiệu quả hơn để làm cho nó private static final Letter[] VALUES = ....
Stephen C

4
Mảng trong Java là có thể thay đổi, vì vậy nếu bạn có một trường mảng và trả về nó trong một phương thức công khai, người gọi có thể sửa đổi nó và nó sửa đổi tệp riêng tư để bạn cần sao chép mảng. Nếu bạn gọi phương thức đó nhiều lần thì đó có thể là một vấn đề vì vậy bạn hãy đặt nó vào một danh sách bất biến thay vì để tránh việc sao chép phòng thủ không cần thiết.
cletus

1
@cletus: Enum.values ​​() sẽ trả về một mảng mới trên mỗi lệnh gọi, do đó không cần phải bọc nó trước khi chuyển / sử dụng nó ở những nơi khác.
Chii

5
Thư cuối cùng tĩnh riêng tư [] GIÁ TRỊ ... là OK. Nó là riêng tư vì vậy nó không thể thay đổi. Bạn chỉ cần phương thức RandomLetter () công khai rõ ràng trả về một giá trị duy nhất. Stephen C đã đúng.
helios

126

Một phương thức duy nhất là tất cả những gì bạn cần cho tất cả các enum ngẫu nhiên của bạn:

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

Bạn sẽ sử dụng cái nào:

randomEnum(MyEnum.class);

Tôi cũng thích sử dụng SecureRandom như:

private static final SecureRandom random = new SecureRandom();

1
Chính xác những gì tôi đang tìm kiếm. Tôi đã làm như câu trả lời được chấp nhận và điều đó đã để lại cho tôi mã soạn sẵn khi tôi cần chọn ngẫu nhiên từ Enum thứ hai của mình. Ngoài ra, đôi khi thật dễ dàng để quên SecureRandom. Cảm ơn.
Siamaster

Bạn đọc suy nghĩ của tôi, chính xác những gì tôi đang tìm kiếm để thêm vào lớp kiểm tra trình tạo thực thể ngẫu nhiên của tôi. Cảm ơn sự giúp đỡ
Roque Sosa

43

Kết hợp các đề xuất của 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<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)];
        }
    }
}

Chỉnh sửa: Rất tiếc, tôi quên tham số loại giới hạn , <E extends Enum<E>>.



1
Tôi biết câu trả lời rất cũ, nhưng không nên E extends Enum<E>?
Lino - Bầu chọn đừng nói Cảm ơn

1
@Lino: Đã chỉnh sửa cho rõ ràng; Tôi không nghĩ rằng nó cần thiết cho loại suy luận chính xác của tham số bị ràng buộc, nhưng tôi hoan nghênh sự điều chỉnh; cũng lưu ý rằng new RandomEnum<>(Season.class)được phép kể từ Java 7.
thùng rác

Lớp đơn này RandomEnumsẽ tốt như một thư viện vi mô, nếu bạn muốn gói nó và xuất bản lên trung tâm.
Greg Chabala


10

Đồng ý với Stphen C & helios. Cách tốt hơn để tìm nạp phần tử ngẫu nhiên từ Enum là:

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

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

5

Đây có lẽ là cách ngắn gọn nhất để đạt được mục tiêu của bạn. Tất cả những gì bạn cần làm là gọi Letter.getRandom()và bạn sẽ nhận được một lá thư enum ngẫu nhiên.

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

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

4

Có lẽ dễ nhất là có một hàm để chọn một giá trị ngẫu nhiên từ một mảng. Điều này là chung chung hơn, và là đơn giản để gọi.

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

Gọi như vậy:

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

4

Đây là một phiên bản sử dụng shuffle và stream

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

4

Giải pháp đơn giản

MyEnum.values().random()

random()là một chức năng mở rộng mặc định được bao gồm trong cơ sở Kotlin trên Collectionđối tượng. Liên kết tài liệu Kotlin

Nếu bạn muốn đơn giản hóa nó bằng chức năng mở rộng, hãy thử điều này:

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

// Then call
random<MyEnum>()

Để làm cho nó tĩnh trên lớp enum của bạn. Đảm bảo nhập my.package.randomtệp enum của bạn

MyEnum.randomValue()

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

Nếu bạn cần làm điều đó từ một ví dụ của enum, hãy thử tiện ích mở rộng này

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

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

3

Nếu bạn làm điều này để kiểm tra, bạn có thể sử dụng Quickcheck ( đây là cổng Java tôi đang làm việc ).

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

TimeUnit anyEnumValue = anyEnumValue(TimeUnit.class); //one value

Nó hỗ trợ tất cả các kiểu nguyên thủy, thành phần kiểu, bộ sưu tập, các hàm phân phối khác nhau, giới hạn, v.v. Nó có hỗ trợ cho người chạy thực thi nhiều giá trị:

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

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

Ưu điểm của Quickcheck là bạn có thể xác định các thử nghiệm dựa trên thông số kỹ thuật nơi TDD đơn giản hoạt động với các kịch bản.


trông hấp dẫn. Tôi sẽ phải thử.
Nick Heiner

Bạn có thể gửi thư cho tôi nếu một cái gì đó không hoạt động. Bạn nên sử dụng phiên bản 0,5b.
Thomas Jung

2

Nó hung dữ để thực hiện một chức năng ngẫu nhiên trên enum.

public enum Via {
    A, B;

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

và sau đó bạn gọi nó từ lớp bạn cần nó như thế này

public class Guardia{
private Via viaActiva;

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

2

Tôi sẽ sử dụng điều này:

private static Random random = new Random();

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

1

Tôi đoán rằng phương thức trả về một dòng này đủ hiệu quả để được sử dụng trong một công việc đơn giản như vậy:

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());
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.