CLOVER🍀

That was when it all began.

Random/ThreadLocalRandom/SecureRandom

JDK 7から、ThreadLocalRandomなるクラスが追加されてたんですね…知らんかった…。

というわけで、JDK 7だと乱数生成として使えるクラスは、3つなのかな?
Math.randomも、結局Random使ってますしね。

Random
http://docs.oracle.com/javase/jp/7/api/java/util/Random.html

ThreadLocalRandom
http://docs.oracle.com/javase/jp/7/api/java/util/concurrent/ThreadLocalRandom.html

SecureRandom
http://docs.oracle.com/javase/jp/7/api/java/security/SecureRandom.html

いずれのクラスもRandom(自身を除いて)のサブクラスですので、インスタンス生成/取得後の使い方は、だいたい同じです。

ちょこっと使ってみましょう。

使うimportは、こんな感じ。

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

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

場合によっては、「NoSuchAlgorithmException」は要らんですが…。

Random

最もオーソドックスなやつ。

Random random = new Random();
System.out.printf("Random#nextInt => %d%n",
                  random.nextInt());
System.out.printf("Random#nextInt => %d%n",
                  random.nextInt());
System.out.printf("Random#nextInt(int) => %d%n",
                  random.nextInt(100));

ThreadLocalRandom

Randomクラス自体はスレッドセーフですが、複数スレッドで使いたい場合はThreadごとにRandomを持った方がいいんじゃあ?ってことで追加されたんでしょうね、ThreadLocalRandomは。

java.util.Random のインスタンスはスレッドセーフです。ただし、複数のスレッドで同じ java.util.Random インスタンスを並行して使用すると、競合が発生してパフォーマンスが低下する可能性があります。マルチスレッド設計では、代わりに ThreadLocalRandom を使用することを検討してください。

http://docs.oracle.com/javase/jp/7/api/java/util/Random.html

文字通り、ThreadLocalにRandomを保持しているようなイメージです。

サンプル。基本、Randomと同じメソッドが使えます。

System.out.printf("ThreadLocalRandom#nextInt => %d%n",
                  ThreadLocalRandom.current().nextInt());
System.out.printf("ThreadLocalRandom#nextInt => %d%n",
                  ThreadLocalRandom.current().nextInt());
System.out.printf("ThreadLocalRandom#nextInt(int) => %d%n",
                  ThreadLocalRandom.current().nextInt(100));
System.out.printf("ThreadLocalRandom#nextInt(int, int) => %d%n",
                  ThreadLocalRandom.current().nextInt(0, 100));

ポイントは、インスタンスを取得する時に都度

ThreadLocalRandom.current()

とすること。currentメソッドで、ThreadLocalから対応するRandomを取得するので、この戻り値のインスタンスをフィールドとかに保持してしまうと、良くないと思われます。

あと、

public double nextDouble(double least, double bound)
public int nextInt(int least, int bound)
public long nextLong(long least, long bound)

みたいな、下限と上限を指定できるメソッドが、しれっと追加されています。どれも、下限は戻り値に含むけれど、上限は含まないという感じみたいです。

このクラスのインスタンスの生成自体に手を出せないためだと思いますが、setSeed(long seed)メソッドは未サポートです。

SecureRandom

暗号化用に強化された乱数ジェネレータということで。あんまり使ったことありませんが…。

インスタンスの生成は、引数なし、またはシードとなるbyte配列を与えたコンストラクタか、アルゴリズムを指定するgetInstanceメソッドで行います。

try {
    SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
    System.out.printf("SecureRandom#nextInt => %d%n",
                      secureRandom.nextInt());
    System.out.printf("SecureRandom#nextInt(int) => %d%n",
                      secureRandom.nextInt(100));
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
}

アルゴリズムを指定した場合は、NoSuchAlgorithmExceptionがスローされる可能性がありますが、コンストラクタでnewする場合には例外処理は不要です。

ちなみに、アルゴリズムを指定する場合は「SHA1PRNG」固定だということですが…。

SecureRandom自体はあまり使ったことがありませんが、TomcatのセッションID生成に使われていますね。雰囲気、こんな感じで。

byte[] bytes = new byte[16];
secureRandom.nextBytes(bytes);

StringBuilder builder = new StringBuilder();
for (byte b : bytes) {
    byte b1 = (byte)((b & 0xf0) >> 4);
    byte b2 = (byte)(b & 0x0f);

    if (b1 < 10) {
        builder.append((char) ('0' + b1));
    } else {
        builder.append((char) ('A' + (b1 - 10)));
    }

    if (b2 < 10) {
        builder.append((char) ('0' + b2));
    } else {
        builder.append((char) ('A' + (b2 - 10)));
    }
}

System.out.printf("SecureRandom#nextBytes to SessionId? => %s%n",
                  builder);