ランダム抽出は「誰を選ぶかを“公平に”決める」技
ランダム抽出は、ざっくり言うと
「コレクションの中から、ランダムに要素を取り出す」処理です。
テスト用にランダムなユーザーを 1 人選びたい。
キャンペーン当選者をランダムに 10 人選びたい。
おすすめ候補の中から、ランダムに 3 件だけ表示したい。
こういう「誰を選ぶかを固定したくない」「偏りを減らしたい」場面で、
ランダム抽出ユーティリティがあると、業務コードがかなり書きやすくなります。
基本形:List から 1 件だけランダムに取り出す
インデックスをランダムに決める
一番シンプルな「ランダム抽出」は、
「0〜size-1 のランダムなインデックスを決めて、その要素を返す」です。
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public final class Randoms {
private Randoms() {}
public static <T> T pickOne(List<T> source) {
if (source == null || source.isEmpty()) {
return null;
}
int index = ThreadLocalRandom.current().nextInt(source.size());
return source.get(index);
}
}
Java使い方はこうなります。
List<String> names = List.of("山田", "佐藤", "鈴木", "田中");
String randomName = Randoms.pickOne(names);
System.out.println(randomName); // 毎回どれか1人
Javaここでの重要ポイントは二つです。
一つ目は、「nextInt(source.size()) が 0 以上 size 未満のランダムなインデックスを返す」ということです。
これにより、すべての要素が同じ確率で選ばれます。
二つ目は、「ThreadLocalRandom.current() を使っている」ことです。new Random() を毎回 new するより、スレッドごとの乱数生成器を使う ThreadLocalRandom の方が、実務では扱いやすくパフォーマンスも良いことが多いです。
複数件を「重複なし」でランダム抽出する
シャッフル+先頭から N 件、が一番分かりやすい
「1 件」ではなく「ランダムに 5 件欲しい」「当選者を 10 人選びたい」といった場合、
重複なしで取りたいことがほとんどです。
一番分かりやすいのは「シャッフルしてから先頭 N 件を取る」方法です。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public final class Randoms {
private Randoms() {}
public static <T> List<T> pickMany(List<T> source, int count) {
if (source == null || source.isEmpty() || count <= 0) {
return List.of();
}
if (count >= source.size()) {
// 要求数が多すぎる場合は全部返す
return new ArrayList<>(source);
}
List<T> copy = new ArrayList<>(source);
Collections.shuffle(copy); // ランダム順に並べ替え
return copy.subList(0, count);
}
}
Java使い方はこうです。
List<String> names = List.of("山田", "佐藤", "鈴木", "田中", "高橋");
List<String> winners = Randoms.pickMany(names, 2);
System.out.println(winners); // 例: [鈴木, 山田]
Javaここで深掘りしたい重要ポイントは三つです。
一つ目は、「シャッフル+先頭 N 件」で“重複なしのランダム抽出”を自然に表現できていること」です。
シャッフルで順番をランダムにしてから、上から N 件取るだけなので、ロジックが直感的です。
二つ目は、「元の List を直接シャッフルせず、コピーを作ってからシャッフルしている」ことです。Collections.shuffle は List を破壊的に並べ替えるので、元の順番を壊さないように new ArrayList<>(source) を挟んでいます。
三つ目は、「count >= source.size() のときは“全部返す”という仕様をユーティリティ側で決めている」ことです。
「欲しい数が多すぎるときにどうするか」を一箇所で決めておくと、呼び出し側が迷わなくなります。
再現性のあるランダム抽出(テスト用)
シード付き Random を使う
テストや検証では、「ランダムだけど、毎回同じ結果になってほしい」ことがあります。
その場合は、Random にシード(種)を渡して使います。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public final class Randoms {
private Randoms() {}
public static <T> List<T> pickMany(List<T> source, int count, long seed) {
if (source == null || source.isEmpty() || count <= 0) {
return List.of();
}
if (count >= source.size()) {
return new ArrayList<>(source);
}
List<T> copy = new ArrayList<>(source);
Random random = new Random(seed);
Collections.shuffle(copy, random); // シード付きシャッフル
return copy.subList(0, count);
}
}
Java使い方はこうです。
List<String> names = List.of("山田", "佐藤", "鈴木", "田中", "高橋");
List<String> winners1 = Randoms.pickMany(names, 2, 12345L);
List<String> winners2 = Randoms.pickMany(names, 2, 12345L);
System.out.println(winners1); // 毎回同じ結果
System.out.println(winners2); // winners1 と同じ
Javaここでの重要ポイントは、
「同じシードを使えば、同じ“ランダム抽出結果”を再現できる」ということです。
テストコードで「ランダム抽出のロジックは使いたいが、結果は固定したい」ときに、
このパターンを使うととても便利です。
業務での使いどころ
例1:おすすめ候補からランダムに 3 件だけ表示する
スコア順に並んだおすすめ候補が 100 件あるとして、
その中からランダムに 3 件だけ表示したい、というケース。
List<Product> candidates = findRecommendedProducts(userId);
List<Product> random3 = Randoms.pickMany(candidates, 3);
Javaここでのポイントは、
「スコア順の“上位 100 件”まではロジックで決めて、その中から“どれを見せるか”はランダムにする」という設計が簡単に書けることです。
例2:キャンペーン当選者をランダムに選ぶ
応募者一覧から、当選者を 10 人ランダムに選ぶ。
List<User> applicants = findApplicants(campaignId);
List<User> winners = Randoms.pickMany(applicants, 10);
Javaここでのポイントは、
「重複なしで公平に選ぶ」という要件を、ユーティリティで保証していることです。
呼び出し側は「何人当選させるか」だけを考えればよくなります。
ランダム抽出の注意点
「本当にランダムでいいのか?」と「偏り」を意識する
ランダム抽出は便利ですが、
業務では次のような点を一度考えた方がいいです。
ランダムなので、「たまたま同じ人ばかり選ばれる」こともありうる。
ログや問い合わせ対応のときに、「なぜこの人が選ばれたのか」を説明しづらい。
セキュリティ的に厳密なランダム(暗号論的乱数)が必要な場面では、SecureRandom を使うべきこともある。
特に「抽選」「当選」などユーザーに影響が大きい処理では、
「ランダムの方法」「抽選ロジック」を仕様としてきちんと決めておくことが大事です。
まとめ:ランダム抽出ユーティリティで身につけてほしい感覚
ランダム抽出は、
単に「適当に選ぶテクニック」ではなく、
「誰を選ぶかを、意図的に“公平さ”と“再現性”のバランスを取りながら決める技術」です。
1 件だけなら「ランダムなインデックスで get」という基本形を押さえる。
複数件・重複なしなら「シャッフル+先頭 N 件」というパターンをユーティリティ化する。
テストや検証では、シード付き Random で「再現性のあるランダム抽出」を使う。
元の List を壊さないように、必ずコピーしてからシャッフルする。
「どこでランダムにするか」「どこは固定にするか」を、ビジネスの意図とセットで設計する。
あなたのコードのどこかに、new Random().nextInt(...) をその場しのぎで書いている箇所があれば、
それを一度「ランダム抽出ユーティリティ」に整理できないか眺めてみてください。
その小さな整理が、
「ランダムさを、ちゃんと“設計された振る舞い”として扱えるエンジニア」への、
確かな一歩になります。

