覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Java - 配列をシャッフルする

Javaで配列をシャッフルするサンプルです。


Listをシャッフルする

Javaで、List内部の要素をシャッフル(ランダムに入れ替え)するには、
Java標準で用意されている Collections.shuffle のAPIを利用すればよいです。


コードサンプル

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class ShuffleListSample {

    public static void main(String[] args) {
        List<Integer> targetList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));

        // シャッフルする前
        printArray(targetList, "No Shuffle");

        // シャッフル1回目
        Collections.shuffle(targetList);
        printArray(targetList, "Shuffle 1 ");

        // シャッフル2回目
        Collections.shuffle(targetList);
        printArray(targetList, "Shuffle 2 ");

        // シャッフル3回目
        Collections.shuffle(targetList);
        printArray(targetList, "Shuffle 3 ");
    }

    private static void printArray(List<Integer> list, String headerComment) {
        System.out.printf("%s -> %s\n\n", headerComment, list);
    }
}


実行結果

No Shuffle -> [1, 2, 3, 4, 5, 6, 7, 8, 9]

Shuffle 1  -> [6, 7, 1, 5, 8, 3, 2, 4, 9]

Shuffle 2  -> [5, 2, 9, 3, 1, 7, 4, 6, 8]

Shuffle 3  -> [4, 1, 7, 2, 8, 6, 5, 3, 9]


配列をシャッフルする場合

Listの場合は、標準でシャッフルするAPIが用意されていますが、配列の場合そのようなAPIが用意されていません。
以下のようなメソッドを用意しましょう。(以下はint配列用です)

public static void shuffle(int[] array) {
    // 配列が空か1要素ならシャッフルしようがないので、そのままreturn
    if (array.length <= 1) {
        return;
    }

    // Fisher–Yates shuffle
    Random rnd = ThreadLocalRandom.current();
    for (int i = array.length - 1; i > 0; i--) {
        int index = rnd.nextInt(i + 1);
        // 要素入れ替え(swap)
        int tmp = array[index];
        array[index] = array[i];
        array[i] = tmp;
    }
}

上記は、Fisher–Yates shuffle(フィッシャー - イェーツのシャッフル)というアルゴリズムでシャッフルしています。


実行のためのサンプルコードは以下の通りです。

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

public class FisherYatesShuffleSample {

    public static void main(String[] args) {
        int[] targetArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};

        // シャッフルする前
        printArray(targetArray, "No Shuffle");

        // シャッフル1回目
        shuffle(targetArray);
        printArray(targetArray, "Shuffle 1 ");

        // シャッフル2回目
        shuffle(targetArray);
        printArray(targetArray, "Shuffle 2 ");

        // シャッフル3回目
        shuffle(targetArray);
        printArray(targetArray, "Shuffle 3 ");
    }

    public static void shuffle(int[] array) {
        // 配列が空か1要素ならシャッフルしようがないのので、そのままreturn
        if (array.length <= 1) {
            return;
        }

        // Fisher–Yates shuffle
        Random rnd = ThreadLocalRandom.current();
        for (int i = array.length - 1; i > 0; i--) {
            int index = rnd.nextInt(i + 1);
            // 要素入れ替え(swap)
            int tmp = array[index];
            array[index] = array[i];
            array[i] = tmp;
        }
    }

    private static void printArray(int[] array, String headerComment) {
        System.out.printf("%s -> %s\n\n", headerComment, Arrays.toString(array));
    }
}


実行結果

No Shuffle -> [1, 2, 3, 4, 5, 6, 7, 8, 9]

Shuffle 1  -> [2, 3, 8, 4, 9, 5, 6, 1, 7]

Shuffle 2  -> [9, 7, 5, 2, 6, 3, 4, 1, 8]

Shuffle 3  -> [2, 8, 5, 4, 1, 6, 7, 3, 9]


まとめ

Fisher–Yates shuffle で配列をシャッフルすることができるようになりました。