Java proporciona soporte nativo para UUID (Identificador Único Universal) a través de la clase java.util.UUID. Esta guía completa te mostrará cómo generar, validar y trabajar con UUIDs en tus aplicaciones Java, desde casos básicos hasta implementaciones avanzadas.
Los UUID son identificadores de 128 bits que son únicos a nivel global, lo que los hace perfectos para sistemas distribuidos, claves primarias de bases de datos y cualquier escenario que requiera identificadores únicos sin coordinación central.
La forma más simple de generar un UUID aleatorio (Versión 4) en Java usando UUID.randomUUID(). Este método es thread-safe y genera UUIDs criptográficamente fuertes.
import java.util.UUID;
public class UUIDGenerator {
public static void main(String[] args) {
// Generar un UUID aleatorio (Versión 4)
UUID uuid = UUID.randomUUID();
System.out.println("UUID generado: " + uuid.toString());
// Convertir a string sin guiones
String uuidSinGuiones = uuid.toString().replace("-", "");
System.out.println("UUID sin guiones: " + uuidSinGuiones);
}
}
UUID.randomUUID() genera un UUID Versión 4 usando un generador de números aleatorios criptográficamente fuerte. El UUID resultante está garantizado ser único con una probabilidad extremadamente alta.
Genera múltiples UUIDs de manera eficiente para procesamiento por lotes, importación de datos o inicialización de bases de datos. Ideal para cuando necesitas crear muchos identificadores únicos de una vez.
import java.util.UUID;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GeneradorMasivoUUID {
// Enfoque tradicional
public static List<UUID> generarMultiplesUUIDs(int cantidad) {
List<UUID> uuids = new ArrayList<>();
for (int i = 0; i < cantidad; i++) {
uuids.add(UUID.randomUUID());
}
return uuids;
}
// Enfoque basado en streams
public static List<UUID> generarUUIDsStream(int cantidad) {
return Stream.generate(UUID::randomUUID)
.limit(cantidad)
.collect(Collectors.toList());
}
public static void main(String[] args) {
// Generar 10 UUIDs
List<UUID> listaUuids = generarUUIDsStream(10);
// Imprimir todos los UUIDs generados
listaUuids.forEach(uuid -> System.out.println(uuid.toString()));
}
}
Este ejemplo muestra enfoques tradicionales y modernos de Java 8+ para la generación masiva de UUID. El enfoque basado en streams es más conciso y funcional, mientras que el enfoque tradicional ofrece más control sobre el proceso de generación.
Crea UUIDs determinísticos basados en nombres (Versión 3), convierte entre strings y UUIDs, y accede a metadatos como versión y variante del UUID.
import java.util.UUID;
import java.nio.charset.StandardCharsets;
public class UUIDBasadoEnNombre {
public static void main(String[] args) {
// Crear UUID basado en nombre (Versión 3 - MD5)
String namespace = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
String nombre = "ejemplo.com";
UUID uuid3 = UUID.nameUUIDFromBytes(
(namespace + nombre).getBytes(StandardCharsets.UTF_8)
);
System.out.println("UUID v3: " + uuid3);
// Crear desde string
String uuidString = "550e8400-e29b-41d4-a716-446655440000";
try {
UUID desdeString = UUID.fromString(uuidString);
System.out.println("UUID desde string: " + desdeString);
// Obtener metadatos del UUID
System.out.println("Versión: " + desdeString.version());
System.out.println("Variante: " + desdeString.variant());
// Comparar UUIDs
UUID otro = UUID.randomUUID();
System.out.println("Resultado comparación: " + desdeString.compareTo(otro));
} catch (IllegalArgumentException e) {
System.err.println("Formato UUID inválido: " + e.getMessage());
}
}
}
Los UUID basados en nombre son determinísticos, lo que significa que la misma entrada siempre producirá el mismo UUID. Esto es útil para crear identificadores consistentes basados en datos existentes.
Implementa generadores UUID personalizados para casos de uso específicos, incluyendo UUIDs basados en tiempo y UUIDs secuenciales optimizados para rendimiento de bases de datos.
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicLong;
public class GeneradorUUIDPersonalizado {
// Generar UUID basado en tiempo (similar a Versión 1)
public static UUID generarUUIDBasadoEnTiempo() {
long mostSigBits = System.currentTimeMillis() << 32;
mostSigBits |= ThreadLocalRandom.current().nextInt() & 0xFFFFFFFFL;
long leastSigBits = ThreadLocalRandom.current().nextLong();
// Establecer bits de versión (1) y variante
mostSigBits &= ~0xF000L;
mostSigBits |= 0x1000L; // Versión 1
leastSigBits &= ~0xC000000000000000L;
leastSigBits |= 0x8000000000000000L; // Variante 10
return new UUID(mostSigBits, leastSigBits);
}
// Generador de UUID secuencial para optimización de BD
public static class GeneradorUUIDSecuencial {
private final AtomicLong contador = new AtomicLong(0);
private final long timestamp = System.currentTimeMillis();
public UUID siguiente() {
ByteBuffer buffer = ByteBuffer.allocate(16);
buffer.putLong(timestamp);
buffer.putLong(contador.incrementAndGet());
buffer.rewind();
long mostSigBits = buffer.getLong();
long leastSigBits = buffer.getLong();
return new UUID(mostSigBits, leastSigBits);
}
}
public static void main(String[] args) {
// Generar UUID personalizado basado en tiempo
UUID basadoEnTiempo = generarUUIDBasadoEnTiempo();
System.out.println("UUID basado en tiempo: " + basadoEnTiempo);
// Generar UUIDs secuenciales
GeneradorUUIDSecuencial genSec = new GeneradorUUIDSecuencial();
for (int i = 0; i < 5; i++) {
System.out.println("UUID secuencial " + i + ": " + genSec.siguiente());
}
}
}
Los generadores UUID personalizados pueden optimizarse para casos de uso específicos. Los UUID secuenciales mejoran el rendimiento del índice de la base de datos, mientras que los UUID basados en tiempo pueden incluir información de marca de tiempo para ordenamiento y análisis.
Aquí hay una clase de utilidad completa para operaciones comunes con UUID en aplicaciones Java:
import java.util.UUID;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class UtilidadesUUID {
// Guardar UUIDs en archivo de texto
public static void guardarUUIDsEnArchivo(String nombreArchivo, int cantidad) {
try (PrintWriter escritor = new PrintWriter(new FileWriter(nombreArchivo))) {
for (int i = 0; i < cantidad; i++) {
escritor.println(UUID.randomUUID().toString());
}
System.out.println("Guardados " + cantidad + " UUIDs en " + nombreArchivo);
} catch (IOException e) {
System.err.println("Error al escribir archivo: " + e.getMessage());
}
}
// Guardar como CSV con metadatos
public static void guardarComoCSV(String nombreArchivo, int cantidad) {
try (PrintWriter escritor = new PrintWriter(new FileWriter(nombreArchivo))) {
escritor.println("Indice,UUID,Timestamp,Version");
for (int i = 0; i < cantidad; i++) {
UUID uuid = UUID.randomUUID();
escritor.printf("%d,%s,%d,%d%n",
i + 1,
uuid.toString(),
System.currentTimeMillis(),
uuid.version());
}
System.out.println("Guardados " + cantidad + " UUIDs en CSV");
} catch (IOException e) {
System.err.println("Error al escribir CSV: " + e.getMessage());
}
}
// Leer UUIDs desde archivo
public static List<UUID> leerUUIDsDesdeArchivo(String nombreArchivo) {
try {
return Files.lines(Paths.get(nombreArchivo))
.map(String::trim)
.filter(linea -> !linea.isEmpty())
.map(UUID::fromString)
.collect(Collectors.toList());
} catch (IOException e) {
System.err.println("Error al leer archivo: " + e.getMessage());
return List.of();
}
}
// Convertir UUID a diferentes formatos
public static String aBase64(UUID uuid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return java.util.Base64.getEncoder()
.encodeToString(bb.array())
.replace("=", "");
}
}
Esta clase de utilidad proporciona funciones esenciales para trabajar con UUIDs, incluyendo operaciones de E/S de archivos, conversiones de formato y capacidades de procesamiento por lotes.
Valida strings UUID usando expresiones regulares y el parser nativo de Java. Esencial para validar entrada de usuarios y garantizar la integridad de datos.
import java.util.UUID;
import java.util.regex.Pattern;
public class ValidadorUUID {
// Patrón regex para validación de UUID
private static final Pattern PATRON_UUID = Pattern.compile(
"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
);
// Patrón regex para UUID sin guiones
private static final Pattern UUID_SIN_GUIONES = Pattern.compile(
"^[0-9a-fA-F]{32}$"
);
// Validar usando regex
public static boolean esUUIDValido(String uuid) {
if (uuid == null) return false;
return PATRON_UUID.matcher(uuid).matches();
}
// Validar usando parser UUID
public static boolean intentarParsearUUID(String uuid) {
try {
UUID.fromString(uuid);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
// Normalizar formato UUID
public static String normalizarUUID(String entrada) {
if (entrada == null) return null;
// Eliminar todos los caracteres no hexadecimales
String limpio = entrada.replaceAll("[^0-9a-fA-F]", "");
// Verificar que tengamos exactamente 32 caracteres hex
if (limpio.length() != 32) {
throw new IllegalArgumentException("Longitud UUID inválida");
}
// Insertar guiones en las posiciones correctas
return String.format("%s-%s-%s-%s-%s",
limpio.substring(0, 8),
limpio.substring(8, 12),
limpio.substring(12, 16),
limpio.substring(16, 20),
limpio.substring(20, 32)
);
}
public static void main(String[] args) {
String[] pruebaUUIDs = {
"550e8400-e29b-41d4-a716-446655440000", // Válido
"550e8400e29b41d4a716446655440000", // Válido (sin guiones)
"no-es-uuid", // Inválido
"550e8400-e29b-41d4-a716", // Inválido (muy corto)
UUID.randomUUID().toString() // Válido
};
for (String prueba : pruebaUUIDs) {
System.out.println("\nProbando: " + prueba);
System.out.println("Validación regex: " + esUUIDValido(prueba));
System.out.println("Validación parser: " + intentarParsearUUID(prueba));
try {
String normalizado = normalizarUUID(prueba);
System.out.println("Normalizado: " + normalizado);
} catch (Exception e) {
System.out.println("Normalización falló: " + e.getMessage());
}
}
}
}
La validación de UUID es crucial para la integridad de los datos. Este ejemplo muestra múltiples enfoques de validación e incluye funcionalidad de normalización para manejar diferentes formatos de UUID.
Al generar UUIDs a escala, el rendimiento se vuelve importante. Aquí te mostramos cómo optimizar la generación de UUID para escenarios de alto rendimiento:
import java.util.UUID;
import java.util.concurrent.*;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
public class RendimientoUUID {
// Thread-local Random para mejor rendimiento en entornos multi-hilo
private static final ThreadLocal<Random> randomLocal =
ThreadLocal.withInitial(() -> new Random());
// Comparar diferentes enfoques de generación UUID
public static void compararGeneracionUUID(int iteraciones) {
// UUID.randomUUID() estándar
long inicio = System.nanoTime();
for (int i = 0; i < iteraciones; i++) {
UUID.randomUUID();
}
long tiempoEstandar = System.nanoTime() - inicio;
// UUID personalizado con ThreadLocalRandom
inicio = System.nanoTime();
for (int i = 0; i < iteraciones; i++) {
generarUUIDRapido();
}
long tiempoPersonalizado = System.nanoTime() - inicio;
System.out.printf("UUID estándar: %d ms%n", tiempoEstandar / 1_000_000);
System.out.printf("UUID personalizado: %d ms%n", tiempoPersonalizado / 1_000_000);
System.out.printf("Mejora de rendimiento: %.2fx%n",
(double) tiempoEstandar / tiempoPersonalizado);
}
// Generación más rápida de UUID para casos no criptográficos
public static UUID generarUUIDRapido() {
Random random = randomLocal.get();
long mostSigBits = random.nextLong();
long leastSigBits = random.nextLong();
// Establecer bits de versión y variante
mostSigBits &= 0xFFFFFFFFFFFF0FFFL; // Limpiar versión
mostSigBits |= 0x0000000000004000L; // Establecer versión 4
leastSigBits &= 0x3FFFFFFFFFFFFFFFL; // Limpiar variante
leastSigBits |= 0x8000000000000000L; // Establecer variante
return new UUID(mostSigBits, leastSigBits);
}
// Generación paralela de UUID
public static CompletableFuture<List<UUID>> generarEnParalelo(int cantidad) {
return CompletableFuture.supplyAsync(() -> {
return IntStream.range(0, cantidad)
.parallel()
.mapToObj(i -> UUID.randomUUID())
.collect(Collectors.toList());
});
}
public static void main(String[] args) throws Exception {
System.out.println("Prueba de Rendimiento de Generación UUID\n");
// Calentamiento
for (int i = 0; i < 10000; i++) {
UUID.randomUUID();
generarUUIDRapido();
}
// Benchmark
compararGeneracionUUID(1_000_000);
// Probar generación paralela
System.out.println("\nPrueba de Generación Paralela:");
long inicio = System.nanoTime();
CompletableFuture<List<UUID>> futuro = generarEnParalelo(100_000);
List<UUID> uuids = futuro.get();
long transcurrido = System.nanoTime() - inicio;
System.out.printf("Generados %d UUIDs en %d ms%n",
uuids.size(), transcurrido / 1_000_000);
}
}
Para aplicaciones de alto rendimiento, considera usar ThreadLocalRandom en lugar de SecureRandom cuando no se requiera fuerza criptográfica. La generación paralela puede mejorar significativamente el rendimiento para operaciones masivas.
Java proporciona excelente soporte integrado para generación y manipulación de UUID. Ya sea que necesites identificadores simples aleatorios o implementaciones personalizadas complejas, la clase UUID ofrece una base sólida. Recuerda elegir el método apropiado según tus requisitos específicos de unicidad, rendimiento y ordenamiento.
Para aplicaciones en producción, considera factores como optimización de almacenamiento en base de datos, requisitos de validación e implicaciones de rendimiento. Los ejemplos en esta guía proporcionan un conjunto completo de herramientas para trabajar con UUIDs en aplicaciones Java.
¿Necesitas generar UUIDs rápidamente? Prueba nuestro generador de UUID en línea para crear UUIDs al instante sin necesidad de programar.