Generar UUID en Java: Guía Completa para Desarrolladores

Introducción a UUID en Java

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.

Métodos de Generación de UUID en Java

Generación Básica de UUID

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.

Generación Masiva de UUIDs

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.

Generación de UUID Basada en Nombre

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.

Generación Personalizada de UUID

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.

Funciones de Utilidad UUID

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.

Validación de UUID

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.

Consideraciones de Rendimiento

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.

Conclusión

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.

© UUIDGenerator.co v1.0 All rights reserved (2025)