Idiomatic way to generate a random alphanumeric string in Kotlin

I can generate a random sequence of numbers in a certain range like the following:

fun ClosedRange<Int>.random() = Random().nextInt(endInclusive - start) +  start
fun generateRandomNumberList(len: Int, low: Int = 0, high: Int = 255): List<Int> {
(0..len-1).map {
(low..high).random()
}.toList()
}

Then I'll have to extend List with:

fun List<Char>.random() = this[Random().nextInt(this.size)]

Then I can do:

fun generateRandomString(len: Int = 15): String{
val alphanumerics = CharArray(26) { it -> (it + 97).toChar() }.toSet()
.union(CharArray(9) { it -> (it + 48).toChar() }.toSet())
return (0..len-1).map {
alphanumerics.toList().random()
}.joinToString("")
}

But maybe there's a better way?

51369 次浏览

Assuming you have a specific set of source characters (source in this snippet), you could do this:

val source = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
java.util.Random().ints(outputStrLength, 0, source.length)
.asSequence()
.map(source::get)
.joinToString("")

Which gives strings like "LYANFGNPNI" for outputStrLength = 10.

The two important bits are

  1. Random().ints(length, minValue, maxValue) which produces a stream of length random numbers each from minValue to maxValue-1, and
  2. asSequence() which converts the not-massively-useful IntStream into a much-more-useful Sequence<Int>.

Without JDK8:

fun ClosedRange<Char>.randomString(length: Int) =
(1..length)
.map { (Random().nextInt(endInclusive.toInt() - start.toInt()) + start.toInt()).toChar() }
.joinToString("")

usage:

('a'..'z').randomString(6)

Lazy folks would just do

java.util.UUID.randomUUID().toString()

You can not restrict the character range here, but I guess it's fine in many situations anyway.

Or use coroutine API for the true Kotlin spirit:

buildSequence { val r = Random(); while(true) yield(r.nextInt(24)) }
.take(10)
.map{(it+ 65).toChar()}
.joinToString("")
('A'..'z').map { it }.shuffled().subList(0, 4).joinToString("")

The best way I think:

fun generateID(size: Int): String {
val source = "A1BCDEF4G0H8IJKLM7NOPQ3RST9UVWX52YZab1cd60ef2ghij3klmn49opq5rst6uvw7xyz8"
return (source).map { it }.shuffled().subList(0, size).joinToString("")
}

Building off the answer from Paul Hicks, I wanted a custom string as input. In my case, upper and lower-case alphanumeric characters. Random().ints(...) also wasn't working for me, as it required an API level of 24 on Android to use it.

This is how I'm doing it with Kotlin's Random abstract class:

import kotlin.random.Random


object IdHelper {


private val ALPHA_NUMERIC = ('0'..'9') + ('A'..'Z') + ('a'..'z')
private const val LENGTH = 20


fun generateId(): String {
return List(LENGTH) { Random.nextInt(0, ALPHA_NUMERIC.size) }
.map { ALPHA_NUMERIC[it] }
.joinToString(separator = "")
}
}

The process and how this works is similar to a lot of the other answers already posted here:

  1. Generate a list of numbers of length LENGTH that correspond to the index values of the source string, which in this case is ALPHA_NUMERIC
  2. Map those numbers to the source string, converting each numeric index to the character value
  3. Convert the resulting list of characters to a string, joining them with the empty string as the separator character.
  4. Return the resulting string.

Usage is easy, just call it like a static function: IdHelper.generateId()

Using Kotlin 1.3:

This method uses an input of your desired string length desiredStrLength as an Integer and returns a random alphanumeric String of your desired string length.

fun randomAlphaNumericString(desiredStrLength: Int): String {
val charPool: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')


return (1..desiredStrLength)
.map{ kotlin.random.Random.nextInt(0, charPool.size) }
.map(charPool::get)
.joinToString("")
}

If you prefer unknown length of alphanumeric (or at least a decently long string length like 36 in my example below), this method can be used:

fun randomAlphanumericString(): String {
val charPool: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
val outputStrLength = (1..36).shuffled().first()


return (1..outputStrLength)
.map{ kotlin.random.Random.nextInt(0, charPool.size) }
.map(charPool::get)
.joinToString("")
}

Since Kotlin 1.3 you can do this:

fun getRandomString(length: Int) : String {
val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
return (1..length)
.map { allowedChars.random() }
.joinToString("")
}
fun randomAlphaNumericString(@IntRange(from = 1, to = 62) lenght: Int): String {
val alphaNumeric = ('a'..'z') + ('A'..'Z') + ('0'..'9')
return alphaNumeric.shuffled().take(lenght).joinToString("")
}

Using Collection.random() from Kotlin 1.3:

// Descriptive alphabet using three CharRange objects, concatenated
val alphabet: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')


// Build list from 20 random samples from the alphabet,
// and convert it to a string using "" as element separator
val randomString: String = List(20) { alphabet.random() }.joinToString("")

To define it for a defined length:

val randomString = UUID.randomUUID().toString().substring(0,15)

where 15 is the number of characters

I use the following code to generate random words and sentences.

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


val randomWord: String = List((1..10).random()) { alphabet.random() }.joinToString("")


val randomSentence: String = (1..(1..10).random()).joinToString(" ") { List((1..10).random()) { alphabet.random() }.joinToString("") }

the question is old already, but I think another great solution (should work since Kotlin 1.3) would be the following:

// Just a simpler way to create a List of characters, as seen in other answers
// You can achieve the same effect by declaring it as a String "ABCDEFG...56789"
val alphanumeric = ('A'..'Z') + ('a'..'z') + ('0'..'9')


fun generateAlphanumericString(length: Int) : String {
// The buildString function will create a StringBuilder
return buildString {
// We will repeat length times and will append a random character each time
// This roughly matches how you would do it in plain Java
repeat(length) { append(alphanumeric.random()) }
}
}

You can use RandomStringUtils.randomAlphanumeric(min: Int, max: Int) -> String from apache-commons-lang3

Here's a cryptographically secure version of it, or so I believe:

fun randomString(len: Int): String {
val random = SecureRandom()
val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray()
return (1..len).map { chars[random.nextInt(chars.size)] }.joinToString("")
}

Building on Fabb111's answer:

fun randomString(length: Int): String =
buildString {
repeat(length) {
append((0 until 36).random().toString(36))
}
}

Numbers can be converted to a different base using the Int.toString(Int).
Base 36 is the numeral system that contains all alphanumeric characters

I used the most voted answer, but after using it for a while I discovered a big problem, since the random strings weren't that random. I had to modify the generation of the random character, since if I only left allowedChars.random() it always generated the same strings, and I couldn't find why. I was reading that because random() was called very quickly, but I don't know how it works when calling it on the list of numbers.

fun getRandomString(): String {
val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
var string = ""
for (i in (1..20)) {
string += allowedChars[(Math.random() * (allowedChars.size - 1)).roundToInt()]
}
return string
}
 // initialise at top
companion object {
private const val allowedCharacters = "0123456789QWERTYUIOPASDFGHJKLZXCVBNM"
}


//pass size and get Random  String Captcha
fun getRandomCaptcha(sizeOfRandomCaptcha: Int): String {
val random = Random()
val sb = StringBuilder(sizeOfRandomCaptcha)
for (i in 0 until sizeOfRandomCaptcha)
sb.append(allowedCharacters[random.nextInt(allowedCharacters.length)])
return sb.toString()
}

Here's a simple function that uses newer Kotlin stdlib functions:

fun randomString(length: Int): String =
CharArray(length) { validChars.random() }.concatToString()




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

This uses:

  • a collection constructor with a initializer function

    CharArray(length) { i -> /* provide a value for element i */ }
    

    This is a one-liner for creating a CharArray and initializing each value using a lambda, It accepts a length, so the CharArray will be initialized with the correct size.

  • The Collection<>.random() extension function, that will pick a random character. This can be seeded if desired.

    validChars.random()
    
  • The initialized CharArray is converted to a string using concatToString(), which was released in version 1.4.