Kotlinでランダムな英数字の文字列を生成する

Kotlinでランダムな英数字文字列を生成する

1. 概要

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

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

2. 依存関係

チュートリアルに入る前に、Apache Commons Lang dependencypom.xml:に追加しましょう。


    org.apache.commons
    commons-lang3
    3.8.1

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

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

3. JavaRandom

まず、use Java Random to generate a random String.の方法を見てみましょう

この例では、スレッドごとにRandomインスタンスを持ち、競合から保護するThreadLocalRandomを使用します。

private val charPool : List = ('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);
}

この例では、getting 10 random alphanumeric characters from character pool by generating their indexes, thenがそれらを結合して、ランダムなString.を作成します。

JDK 7以降、ThreadLocalRandomを使用できます。代わりにjava.util.Randomを使用できます。 But if multiple threads use the same instance of Random, the same seed is shared by multiple threads, causing thread contention.

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

4. KotlinRandom

Kotlin 1.3から、kotlin.random.Randomがマルチプラットフォーム機能として利用可能になりました。 JDK 6および7ではjava.util.Randomを使用し、JDK 8以降では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を使用している場合は、make use of Apache Common Lang libraries to generate a random String:

@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を使用してランダムな値を生成することに注意してください。これは、上記で説明したように暗号的に安全ではありません。 So in case of generating a secured token or value, we can use CryptoRandom in Apache Commons Crypto or Java’s SecureRandom.

このトピックをより詳細にカバーするために、how to generate a random String in Javaに関するチュートリアルもあります。

6. パフォーマンス

これらのそれぞれの注目すべき点は、乱数ジェネレーターをSTRING_LENGTH timesと呼んでいることです。 多くのStringsまたは長いStrings,を作成している場合、これらのアプローチは遅すぎる可能性があります。 ただし、With some extra effortは、ランダムなバイトシーケンスを呼び出すだけで、それらを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.get((bytes[i] and 0xFF.toByte() and (charPool.size-1).toByte()).toInt())
    }.joinToString("")

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

このアプローチを強力にするのは、while we still do STRING_LENGTH lookups to our charPool, we only call on our random generator once.であり、高速であることに加えて、共有インスタンスでのスレッドの競合を減らすこともできます。

また、bytes[i] and 0xFF.toByte() and charPool.size.toByte()は高度に見えるかもしれませんが、ランダムなバイトがcharPool.size()の間にあることを確認する方法にすぎないことに注意してください。

7. 結論

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

いつものように、コードはover on GitHubで見つけることができます。