¿Cómo generar una cadena aleatoria en Java sin volverse loco?
\n\nImagine: está escribiendo un registro en un sitio web. Necesita crear un token único para confirmar el correo electrónico. O generar una contraseña para un nuevo usuario. O simplemente hacer un ID legible para un enlace. Lo primero que viene a la mente es tomar UUID.randomUUID(), quitar los guiones y disfrutar de la vida.
Alto. ¿Sabía que una acción aparentemente simple puede convertirse en un cuello de botella de rendimiento? La pregunta de Stack Overflow "How to generate a random alpha-numeric string" ha acumulado miles de visitas por una razón. El diablo, como siempre, está en los detalles: la elección del generador de números aleatorios, el algoritmo, la gestión de la memoria. En este artículo no solo escribiremos código, lo optimizaremos al máximo.
\n\nPrepare IntelliJ IDEA. ¡Allá vamos!
\n\n1. La forma más simple (pero lenta): UUID
\n\nEmpecemos con lo clásico. UUID genera un valor de 128 bits. Si quitamos los guiones, obtenemos 32 caracteres en formato hexadecimal (0-9, a-f). Esto ya es una cadena alfanumérica, pero con un alfabeto limitado.
public static String generateWithUUID() {\n return UUID.randomUUID().toString().replace("-", "");\n}\n\n\nVentajas: Una línea de código. No hay que pensar.
\nDesventajas: No se puede controlar la longitud. Solo caracteres hexadecimales (16 caracteres del alfabeto). Para contraseñas es débil. Para tokens sirve, pero hay un detalle.
\n\nPor cierto, UUID.randomUUID() usa SecureRandom internamente. Es criptográficamente seguro, pero lento. Si necesita 10 000 cadenas por segundo, este método le decepcionará.
2. RandomStringUtils de Apache Commons Lang
\n\nUn clásico que pasa de proyecto en proyecto. La biblioteca commons-lang3 ofrece un método listo:
import org.apache.commons.lang3.RandomStringUtils;\n\npublic static String generateWithCommons(int length) {\n return RandomStringUtils.randomAlphanumeric(length);\n}\n\n\n¿Bonito? Sí. ¿Rendimiento? No tanto.
\nInternamente, RandomStringUtils usa java.util.Random (no criptográficamente seguro) y crea muchos objetos temporales. Para la mayoría de las tareas es suficiente, pero si genera millones de cadenas, prepárese para pausas del GC.
3. Método personalizado con Random.nextInt()
\n\nEscribamos nuestra propia implementación. La forma más obvia:
\n\nimport java.security.SecureRandom;\nimport java.util.Random;\n\npublic class RandomStringGenerator {\n private static final String ALPHANUMERIC = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";\n private static final int BASE = ALPHANUMERIC.length();\n private final Random random;\n\n public RandomStringGenerator() {\n this.random = new SecureRandom(); // para seguridad criptográfica\n }\n\n public String generate(int length) {\n StringBuilder sb = new StringBuilder(length);\n for (int i = 0; i < length; i++) {\n sb.append(ALPHANUMERIC.charAt(random.nextInt(BASE)));\n }\n return sb.toString();\n }\n}\n\n\n¿Qué es importante aquí?
\n- \n
- Usamos
StringBuildercon capacidad conocida: no habrá expansiones innecesarias. \n SecureRandomgarantiza que la salida no se pueda predecir. Para tokens es imprescindible. \n - El alfabeto está definido como una cadena: 62 caracteres (26+26+10). Se pueden añadir caracteres especiales. \n
Pero hay un problema: SecureRandom puede ser lento en Linux (espera de entropía). Si la velocidad es más importante que la seguridad, use ThreadLocalRandom.current().
4. Optimización con char[] y ThreadLocalRandom
\n\n¿Quiere exprimir al máximo? Abandonamos StringBuilder y trabajamos directamente con un array de caracteres:
import java.util.concurrent.ThreadLocalR