tags:

views:

1297

answers:

3

If I have an enum like this:

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

What is the best way to pick one randomly? It doesn't need to be production quality bulletproof, but a fairly even distribution would be nice.

I could do something like this

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

But is there a better way? I feel like this is something that's been solved before.

+7  A: 

The only thing I would suggest is caching the result of values() because each call copies an array. Also, don't create a Random every time. Keep one. Other than that what you're doing is fine. So:

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));
  }
}
cletus
If you consider it useful, you can make an utility class for doing this. Something like RandomEnum<T extends Enum> with a constructor receiving the Class<T> for creating the list.
helios
I really don't see the point of converting the `values()` array to an unmodifiable list. The `VALUES` object is already encapsulated by virtue of being declared `private`. It would be simpler AND more efficient to make it `private static final Letter[] VALUES = ...`.
Stephen C
Arrays in Java are mutable so if you have a array field and return it in a public method the caller can modify it and it modifies the private filed so you need to defensively copy the array. If you call that method lots of times it can be a problem so you put it in an immutable list instead to avoid unnecessary defensive copying.
cletus
@cletus: Enum.values() will return a new array on every invocation, so there is no need to wrap it before passing/using it in other places.
Chii
@Chii: it's precisely because it returns an array every time that you should create an immutable copy.
cletus
private static final Letter[] VALUES... is OK. It's private so it's inmmutable. You only need the public randomLetter() method which obviously returns a single value. Stephen C is right.
helios
+5  A: 

Combining the suggestions of cletus and helios,

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

Edit: Oops, I forgot the bounded type parameter, <E extends Enum>.

trashgod
+1  A: 

If you do this for testing you could use Quickcheck (this is a Java port I've been working on).

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

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

It supports all primitive types, type composition, collections, different distribution functions, bounds etc. It has support for runners executing multiple values:

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

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

The advantage of Quickcheck is that you can define tests based on a specification where plain TDD works with scenarios.

Thomas Jung
looks intriguing. I'll have to give it a try.
Rosarch
You can mail me if something does not work. You should use the 0.5b version.
Thomas Jung