열거 형에서 임의의 값을 선택 하시겠습니까?


162

내가 이와 같은 열거 형을 가지고 있다면 :

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

무작위로 하나를 선택하는 가장 좋은 방법은 무엇입니까? 생산 품질의 방탄 일 필요는 없지만 상당히 고른 분포가 좋을 것입니다.

나는 이런 식으로 할 수 있습니다

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

그러나 더 좋은 방법이 있습니까? 나는 이것이 이전에 해결 된 것 같은 느낌이 든다.


솔루션에 어떤 문제가 있다고 생각하십니까? 나에게 꽤 좋아 보인다.
대통령 제임스 K. 포크

1
@GregS-문제는 호출 할 때마다 Letter.values()내부 Letter값 배열 의 새 복사본을 만들어야한다는 것 입니다.
Stephen C

답변:


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
유용하다고 생각되면이를 수행하기위한 유틸리티 클래스를 만들 수 있습니다. RandomEnum <T와 같은 것은 목록을 만들기 위해 Class <T>를받는 생성자로 Enum>을 확장합니다.
helios

15
values()배열을 수정할 수없는 목록 으로 변환하는 요점을 실제로 보지 못합니다 . VALUES개체가 이미 선언 된 덕분에 캡슐화됩니다 private. 그것을 만드는 것이 더 간단하고 효율적 private static final Letter[] VALUES = ...입니다.
Stephen C

4
Java의 배열은 변경 가능하므로 배열 필드가 있고 공용 메소드로 리턴하면 호출자가이를 수정할 수 있으며 개인 파일을 수정하여 방어 적으로 배열을 복사해야합니다. 이 메소드를 여러 번 호출하면 문제가 될 수 있으므로 불필요한 방어 복사를 피하기 위해 변경 불가능한 목록에 넣습니다.
cletus December

1
@cletus : Enum.values ​​()는 모든 호출에서 새로운 배열을 반환하므로 다른 곳에서 전달 / 사용하기 전에 랩핑 할 필요가 없습니다.
Chii

5
비공개 정적 최종 문자 [] 값 ...이 정상입니다. 그것은 사적인 것이므로 불가피합니다. 분명히 단일 값을 반환하는 public randomLetter () 메서드 만 있으면됩니다. 스티븐 C가 옳다.
helios

126

단일 임의의 열거 형에 필요한 모든 방법은 다음과 같습니다.

    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
정확히 내가 찾던 것. 나는 받아 들인 대답처럼하고 있었고 두 번째 Enum에서 무작위 화해야 할 때 상용구 코드가 남았습니다. 또한 SecureRandom을 잊어 버리기 쉽습니다. 감사.
Siamaster

내 랜덤 엔터티 생성기 테스트 클래스에 추가하고자하는 것을 정확하게 읽었습니다. 도움을 주셔서 감사합니다
Roque Sosa

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
@Lino : 명확성을 위해 편집되었습니다. 매개 변수 바인딩의 올바른 형식 유추에 필요한 것은 아니라고 생각 하지만 수정을 환영합니다. 또한 참고 new RandomEnum<>(Season.class)자바 7부터 허용된다
trashgod

이 단일 RandomEnum클래스는 번들로 묶고 중앙에 게시하려는 경우 마이크로 라이브러리로 유용합니다.
Greg Chabala


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

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

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()기본 Kotlin에 포함 된 기본 확장 기능 Collection입니다. 코 틀린 문서 링크

확장 기능으로 단순화하려면 다음을 시도하십시오.

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

// Then call
random<MyEnum>()

열거 형 클래스에서 정적으로 만들려면. my.package.random열거 형 파일 로 가져와야 합니다.

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

테스트를 위해이 작업을 수행하면 빠른 검사를 사용할 수 있습니다 ( 이것은 내가 작업 한 Java 포트입니다 ).

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가 시나리오에서 작동 하는 사양을 기반으로 테스트를 정의 할 수 있다는 것 입니다.


흥미로워 보인다. 나는 그것을 시도해야합니다.
Nick Heiner

문제가 해결되지 않으면 저에게 메일을 보내실 수 있습니다. 0.5b 버전을 사용해야합니다.
Thomas Jung

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

2

나는 이것을 사용할 것이다 :

private static Random random = new Random();

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

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());
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.