Kotlinでランダムな英数字の文字列を生成する
1概要
このチュートリアルでは、3つの異なるアプローチ(Java
Random
、Kotlin
Random
、およびApache Commons Lang
RandomStringUtils
)を使用して、Kotlinでランダムな英数字
String
を生成する方法について説明します。
それでは、高性能のアプローチを見ていきましょう。
2依存関係
チュートリアルを始める前に、https://search.maven.org/search?q=g:org.apache.commons%20AND%20a:commons-lang3[Apache Commons Lang依存関係]を
pom.xmlに追加しましょう。 :
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
さらに、後で参照できるように定数を設定することもできます。
const val STRING__LENGTH = 10;
const val ALPHANUMERIC__REGEX = "[a-zA-Z0-9]+";
3 Java
Random
まず最初に、Javaの
Random
を使用してランダムな
String
__.
__を生成する方法を見てみましょう。
この例では、スレッドごとに
Random
インスタンスを持ち、競合から保護する
ThreadLocalRandom
を使用します。
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個のランダムな英数字を取得し、
それらを結合してランダムな__Stringを作成します。
ThreadLocalRandom
はJDK 7
__以降で利用可能です。
代わりに
java.util.Random
を使用できます。ただし、複数のスレッドが同じ
Random__のインスタンスを使用すると、同じシードが複数のスレッドで共有され、スレッドの競合が発生します。
ただし、
ThreadLocalRandom
も
Random
も暗号的に安全ではありません。これは、ジェネレータから返される次の値を推測することが可能なためです。 Javaは乱数を安全に生成するために著しく遅い
java.security.SecureRandom
を提供します。
4コトリン
Random
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を使用している場合は、Apache Common Langライブラリを使用してランダムな
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
を使用してランダムな値を生成しますが、これは前述したように暗号的に安全ではありません。 ** 安全なトークンまたは値を生成する場合は、Apache Commons Cryptoの
CryptoRandom
またはJavaの__SecureRandomを使用できます。
Javaでランダムな
String
を生成する方法
についてのチュートリアルもあります。また、このトピックについて詳しく説明します。
6. パフォーマンス
これらのそれぞれの注目すべき点は、乱数ジェネレータを
__STRING
LENGTH
__timesと呼んでいることです。たくさんの
文字列または長い__文字列を作成している場合、これらのアプローチは遅すぎるかもしれません。
追加の労力を払って
では、単純にランダムなバイトシーケンスを呼び出してから、それらを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)
}
このアプローチを強力にしているのは、**
STRING
LENGTH
の検索を
charPool__に対して行っている間は、ランダムジェネレータを1回だけ呼び出すだけです。
また、
bytes[i]と0xFF.toByte()およびcharPool.size.toByte()
は高度に見えるかもしれませんが、ランダムバイトが
__0
と
charPool.size()__の間にあることを確認するための単なる方法です。
7. 結論
結論として、Kotlinでランダムな英数字の文字列を生成するための3つのアプローチを経て、それぞれのニュアンスを調べました。それから、KotlinとJava APIのために再利用できる高性能なソリューションを検討するためにギアをシフトしました。
いつものように、コードはhttps://github.com/eugenp/tutorials/tree/master/core-kotlin[GitHubに載って]を見つけることができます。