1. 概要

このチュートリアルでは、Java Random 、Kotlin Random 、Apache Commons Langの3つの異なるアプローチを使用して、Kotlinでランダムな英数字Stringを生成する方法について説明します。 RandomStringUtils

次に、高性能アプローチについて説明します。

2. 依存関係

チュートリアルに入る前に、 Apache CommonsLang依存関係pom.xml:に追加しましょう。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

さらに、後で参照できるようにいくつかの定数を設定できます。

const val STRING_LENGTH = 10;
const val ALPHANUMERIC_REGEX = "[a-zA-Z0-9]+";

3. Javaランダム

まず、JavaRandomを使用してランダムな文字列を生成する方法を見てみましょう。

この例では、 ThreadLocalRandom を使用します。これは、スレッドごとに Random インスタンスを持ち、競合を防ぎます。

private val charPool : List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')

@Test
fun givenAStringLength_whenUsingJava_thenReturnAlphanumericString() {
    val randomString = ThreadLocalRandom.current()
     .ints(STRING_LENGTH.toLong(), 0, charPool.size)
     .asSequence()
     .map(charPool::get)
     .joinToString("")

    assert(randomString.matches(Regex(ALPHANUMERIC_REGEX)));
    assertEquals(STRING_LENGTH, randomString.length);
}

この例では、インデックスを生成することにより、文字プールから10個のランダムな英数字を取得し、次にそれらを結合して、ランダムな文字列を作成します。

ThreadLocalRandom JDK7以降で使用可能です 。 使用できます java.util.Random 代わりは。 ただし、複数のスレッドがRandomの同じインスタンスを使用している場合、同じシードが複数のスレッドで共有され、スレッドの競合が発生します。

ただし、ジェネレータから返される次の値を推測できるため、ThreadLocalRandomもRandomも暗号的に安全ではありません。 Javaは、ランダムな値を安全に生成するために、著しく遅いjava.security.SecureRandomを提供します。

4. Kotlinランダム

Kotlin 1.3から、kotlin.random.Randomがマルチプラットフォーム機能として利用可能になりました。 JDK6および7ではjava.util.Randomを使用し、JDK8+ではThreadLocalRandom を使用し、JavascriptではMath.randomを使用します。

同じアプローチでランダムなStringを取得できます。

val randomString = (1..STRING_LENGTH)
  .map { i -> kotlin.random.Random.nextInt(0, charPool.size) }
  .map(charPool::get)
  .joinToString("");

5. Apache Common Lang 

最後に、まだKotlinを使用している場合は、 Apache Common Langライブラリを使用して、ランダムな文字列 を生成できます。

@Test
fun givenAStringLength_whenUsingApacheCommon_thenReturnAlphanumericString() {
    val randomString = RandomStringUtils.randomAlphanumeric(STRING_LENGTH);
 
    assert(randomString.matches(Regex(ALPHANUMERIC_REGEX)));
    assertEquals(STRING_LENGTH, randomString.length);
}

この例では、 RandomStringUtils.randomAlphanumeric を呼び出すだけで、事前定義された長さのStringを取得します。

RandomStringUtils は、 java.util.Random を使用してランダムな値を生成することに注意してください。これは、上記で説明したように暗号的に安全ではありません。 したがって、保護されたトークンまたは値を生成する場合、ApacheCommonsCryptoまたはJavaのSecureRandomでCryptoRandomを使用できます。

Java でランダムな文字列を生成する方法についてのチュートリアルもあり、このトピックをより詳細にカバーしています。

6. パフォーマンス

これらのそれぞれの注目すべき点は、乱数ジェネレーターSTRING_LENGTH回を呼び出していることです。 多くの文字列または長い文字列を作成している場合、これらのアプローチは遅すぎる可能性があります。 ただし、少し手間をかけるだけで、ランダムなバイトシーケンスを呼び出して、それらをcharプールにマップできます。

@Test
fun givenAStringLength_whenUsingRandomForBytes_thenReturnAlphanumericString() {
    val random = SecureRandom()
    val bytes = ByteArray(STRING_LENGTH)
    random.nextBytes(bytes)

    val randomString = (0..bytes.size - 1)
      .map { i -> charPool[random.nextInt(charPool.size)]
    }.joinToString("")

    assert(randomString.matches(Regex(ALPHANUMERIC_REGEX)))
    assertEquals(STRING_LENGTH, randomString.length)
}

このアプローチを強力なものにしているのは、 charPoolに対してSTRING_LENGTHルックアップを実行している間、ランダムジェネレーターを1回だけ呼び出すことです。また、これにより、共有インスタンスでのスレッドの競合が減少する可能性があります。

7. 結論

結論として、Kotlinでランダムな英数字の文字列を生成するために、それぞれのニュアンスを調査する3つのアプローチを実行しました。 次に、ギアをシフトして、KotlinおよびJavaAPIで再利用できる高性能ソリューションを検討しました。

いつものように、コードはGitHubにあります。