Zufällige Elemente aus einer Liste auswählen

Um einem Kunden zufällige Produkte oder verwandte Links anzuzeigen, kann es notwendig sein ein oder mehrere zufällige Elemente aus einer Liste auszuwählen.

Inhaltsverzeichnis

Ein zufälliges Element auswählen

Ein einzelnes zufälliges Element auszuwählen ist relativ einfach. Auf die Elemente einer Liste kann direkt per Index zugegriffen werden und die Länge ist bekannt:

List<String> beerList = Arrays.asList("Fürstenbräu", "Eule", "Flecks", "Bevog", "Gösser");
String beer = beerList.get(new Random().nextInt(beerList.size()));
System.out.println(beer);

Bei dieser Methode fällt auf, dass bei der Abfrage ein neues Random Objekt erzeugt wird. Das ist verhältnismäßig teuer und wenn der Code oft aufgerufen wird, zahlt es sich durchaus aus, den Code in eine eigene Methode auszulagern.

private static Random random = new Random();

public static <T> T getRandomItem(List<T> list) {
    if(list.isEmpty()){
        throw new IllegalArgumentException(
                "Die Liste darf nicht leer sein!");
    }
    T item = list.get(random.nextInt(list.size()));
    return item;
}

Das Random Objekt ist nun in einer eigenen statischen Variable ausgelagert. Über die Synchronisierung der Variable in einem nebenläufigen Szenario brauchen wir uns keine Gedanken zu machen. Allerdings darf die Liste nicht leer sein, da wir sonst kein Element zurückgeben können. Im Fall einer leeren Liste wird eine IllegalArgumentException mit einer Fehlermeldung geworfen.

Mehrere zufällige Elemente auswählen

Um mehrere zufällige Elemente auszuwählen kann die obige Methode getRandomItem() ganz einfach mehrmals aufgerufen werden.

// Nicht optimal!
public static <T> List<T> getRandomItems(List<T> list, int count) {
    List<T> result = new ArrayList<>();
    for (int i = 0; i < count; i++) {
        result.add(getRandomItem(list));
    }
    return result;
}

Das Problem ist, dass Random.nextInt() natürlich mehrmals die selbe Zahl zurückgeben kann und somit die Methode getRandomItems() doppelte Elemente zurückgeben könnte.

Um das zu verhindern, wird ein anderer Ansatz gewählt. Zuerst wird die Liste zufällig sortiert und danach werden einfach die ersten count Elemente zurückgegeben. Java bietet zum zufälligen Sortieren einer Liste eine praktische Utility-Methode: Collections.shuffle().

public static <T> List<T> getRandomShuffledItems(List<T> list, int count) {
    if(list.isEmpty()){
        throw new IllegalArgumentException("Die Liste darf nicht leer sein!");
    }
    if(count > list.size()){
        count = list.size();
    }
    List<T> copy = new ArrayList<>(list);
    List<T> result = new ArrayList<>();
    Collections.shuffle(copy);
    return copy.subList(0, count - 1);
}

Zu beachten ist, dass Collections.shuffle() die Liste direkt verändert. Darum wird eine Kopie der Liste erstellt die dann "geshufflet" wird. Zu beachten ist, dass count maximal so groß sein kann wie die Liste Elemente hat.

Will man mehrere zufällige Elemente auswählen, muss man beachten dass Einträge in einer Liste doppelt vorkommen können. Soll verhindert werden, dass mehrere gleiche Elemente ausgewählt werden, kann die Liste beispielsweise in ein Set umgewandelt werden. Ein Set enthält per Definition nur unterschiedliche Elemente:

public static <T> List<T> getRandomDistinctItems(List<T> list, int count) {
    if(list.isEmpty()){
        throw new IllegalArgumentException("Die Liste darf nicht leer sein!");
    }
    List<T> copy = new ArrayList<>(new HashSet<>(list));
    if(count > copy.size()){
        count = copy.size();
    }
    List<T> result = new ArrayList<>();
    Collections.shuffle(copy);
    return copy.subList(0, count - 1);
}

Ähnliche Artikel