Método eficiente para generar cadenas UUID en JAVA (UUID.randomUUID (). ToString () sin guiones)

154

Me gustaría una utilidad eficiente para generar secuencias únicas de bytes. UUID es un buen candidato pero UUID.randomUUID().toString()genera cosas como44e128a5-ac7a-4c9a-be4c-224b6bf81b20 que es bueno, pero preferiría una cadena sin guiones.

Estoy buscando una forma eficiente de generar cadenas aleatorias, solo a partir de caracteres alfanuméricos (sin guiones u otros símbolos especiales).

Maxim Veksler
fuente
38
¿Por qué deben eliminarse los guiones para que tal UUID se transmita a través de HTTP?
Bruno
66
No pensé que los guiones debían eliminarse en HTTP en general ... ¿qué bit te está causando problemas?
Jon Skeet
2
Tal vez en un entorno móvil, si todavía paga por cada byte transmitido, y utiliza una red de bajo ancho de banda y alta latencia, guardar 4 bytes sigue siendo importante en algunos escenarios ...
Guido
2
Quiero que se eliminen los guiones porque luego usamos la cadena UUID como identificador de solicitud único, es mucho más fácil trabajar solo con caracteres decimales hexadecimales que [a-f0-9-].
Maxim Veksler
He eliminado la parte HTTP porque no es relevante (como explicó Maxim), solo confunde a los lectores (como se puede ver tanto en los comentarios como en las respuestas).
Ondra Žižka

Respuestas:

274

Esto lo hace:

public static void main(String[] args) {
    final String uuid = UUID.randomUUID().toString().replace("-", "");
    System.out.println("uuid = " + uuid);
}
Steve McLeod
fuente
Por ejemplo, Mongodb no usa guiones en ObjectID. Por lo tanto, eliminar guiones puede ser útil para la API.
Alexey Ryazhskikh
1
Te daré una razón por la cual. Hay una API con la que estoy trabajando (perfil alto, bien conocido) que no permite guiones en su UUID. Tienes que despojarlos.
Michael Gaines
19
No es necesario hacer replaceAll, que usa expresiones regulares. Simplemente haga .replace ("-", "")
Craigo
1
sustituir el método de la clase String es un poco lento, creo
bmscomp
@bmscomp para la primera invocación, es lento, pero para las próximas invocaciones, no hay problema.
gaurav
30

No es necesario eliminar los guiones de la solicitud HTTP, como puede ver en la URL de este hilo. Pero si desea preparar una URL bien formada sin depender de los datos, debe usar URLEncoder.encode (String data, String encoding) en lugar de cambiar la forma estándar de sus datos. Para la representación de cadena UUID, los guiones son normales.

Donz
fuente
"No es necesario eliminar los guiones de la solicitud HTTP, como puede ver en la URL de este hilo". No entiendo, a menos que Stack Overflow haya usado previamente UUID en sus URL.
RenniePet
1
No es que la url sea un UUID, sino que tiene guiones:http://stackoverflow.com/questions/3804591/efficient-method-to-generate-uuid-string-in-java-uuid-randomuuid-tostring-w?rq=1
Octavia Togami
12

Terminé escribiendo algo propio basado en la implementación de UUID.java. Tenga en cuenta que no estoy generando un UUID , sino solo una cadena hexadecimal aleatoria de 32 bytes de la manera más eficiente que se me ocurra.

Implementación

import java.security.SecureRandom;
import java.util.UUID;

public class RandomUtil {
    // Maxim: Copied from UUID implementation :)
    private static volatile SecureRandom numberGenerator = null;
    private static final long MSB = 0x8000000000000000L;

    public static String unique() {
        SecureRandom ng = numberGenerator;
        if (ng == null) {
            numberGenerator = ng = new SecureRandom();
        }

        return Long.toHexString(MSB | ng.nextLong()) + Long.toHexString(MSB | ng.nextLong());
    }       
}

Uso

RandomUtil.unique()

Pruebas

Algunas de las entradas que he probado para asegurarme de que funcionan:

public static void main(String[] args) {
    System.out.println(UUID.randomUUID().toString());
    System.out.println(RandomUtil.unique());

    System.out.println();
    System.out.println(Long.toHexString(0x8000000000000000L |21));
    System.out.println(Long.toBinaryString(0x8000000000000000L |21));
    System.out.println(Long.toHexString(Long.MAX_VALUE + 1));
}
Maxim Veksler
fuente
1
No estoy seguro de por qué esto se ha votado más, este UUID generado sin el "-" en el método más eficiente de todas las opciones escritas aquí. El reemplazo de la cadena no es mejor que la conversión de larga a cadena. Es cierto que ambos son O (n), pero a escala en la que genera millones de uuid por minuto se vuelve significativo.
Maxim Veksler
10

Usé JUG (Java UUID Generator) para generar una identificación única. Es único en todas las JVM. Bastante bueno de usar. Aquí está el código para su referencia:

private static final SecureRandom secureRandom = new SecureRandom();
private static final UUIDGenerator generator = UUIDGenerator.getInstance();

public synchronized static String generateUniqueId() {
  UUID uuid = generator.generateRandomBasedUUID(secureRandom);

  return uuid.toString().replaceAll("-", "").toUpperCase();
}

Puede descargar la biblioteca desde: https://github.com/cowtowncoder/java-uuid-generator

Sheng Chien
fuente
Para su caso, ¿qué tiene de malo UUID.randomUUID (). ToString ()? También tenga en cuenta que usted (en teoría) disminuye la entropía manteniendo un SecureRandom final estático (lo hace volátil). ¿También por qué sincronizar generateUniqueId? Esto significa que todos sus hilos están bloqueados en este método.
Maxim Veksler
En primer lugar, Safehaus afirma que JUG es más rápido. Y puede generar ID únicos en todas las máquinas que quizás no necesite. Tienen un método basado en el tiempo, que es el más rápido entre todos los métodos. Sí, sincronizado no es necesario aquí porque me di cuenta de que SecureRandom ya es seguro para subprocesos. ¿Por qué declarar static static en SecureRandom disminuiría la entropía? Tengo curiosidad :) Hay más detalles aquí: jug.safehaus.org/FAQ
Sheng Chien
JUG también puede generar UUID basados ​​en números aleatorios; pero las razones principales por las que los desarrolladores prefieren usar una variante basada en el tiempo es que es 10-20x más rápido ( cowtowncoder.com/blog/archives/2010/10/entry_429.html ); o que no confían en la aleatoriedad para producir identificadores únicos (lo cual es divertido)
StaxMan
jug.safehaus.org ya no existe, pero puede encontrar las preguntas frecuentes en raw.github.com/cowtowncoder/java-uuid-generator/3.0/…
Daniel Serodio
+1 por mencionar JUG: he revisado su utilidad, pero es bueno saber que hay algunas java.util.UUIDalternativas serias .
Greg Dubicki
8

Una solución simple es

UUID.randomUUID().toString().replace("-", "")

(Al igual que las soluciones existentes, solo que evita la llamada a String # replaceAll . Aquí no se requiere el reemplazo de la expresión regular, por lo que String # replace siente más natural, aunque técnicamente todavía se implementa con expresiones regulares. Dado que la generación del UUID es más costoso que el reemplazo, no debería haber una diferencia significativa en el tiempo de ejecución).

El uso de la clase UUID es probablemente lo suficientemente rápido para la mayoría de los escenarios, aunque esperaría que alguna variante escrita a mano especializada, que no necesita el procesamiento posterior, sea más rápida. De todos modos, el cuello de botella del cómputo general normalmente será el generador de números aleatorios. En el caso de la clase UUID, utiliza SecureRandom .

Qué generador de números aleatorios utilizar también es una compensación que depende de la aplicación. Si es sensible a la seguridad, SecureRandom es, en general, la recomendación. De lo contrario, ThreadLocalRandom es una alternativa (más rápido que SecureRandom o el antiguo Random , pero no criptográficamente seguro).

Philipp Claßen
fuente
7

Me sorprende ver tantas ideas de reemplazo de cadenas de UUID. Qué tal esto:

UUID temp = UUID.randomUUID();
String uuidString = Long.toHexString(temp.getMostSignificantBits())
     + Long.toHexString(temp.getLeastSignificantBits());

Esta es la forma más rápida de hacerlo, ya que todo toString () de UUID ya es más costoso, sin mencionar la expresión regular que debe analizarse y ejecutarse o el reemplazo con una cadena vacía.

Stephan
fuente
66
Esto no es confiable. La salida será más corta si los bits iniciales son 0.
OG Dude
77
String.format("0x%016x%016x", f.getMostSignificantBits(), f.getLeastSignificantBits())
Galets
@galets Aunque he votado tu comentario para resolver el problema con ceros a la izquierda, me pregunto si esto sería mejor en comparación con la alternativa de reemplazar los guiones usando replace.
igorcadelima
3

Acabo de copiar el método UUID toString () y lo actualicé para eliminar "-" de él. Será mucho más rápido y directo que cualquier otra solución

public String generateUUIDString(UUID uuid) {
    return (digits(uuid.getMostSignificantBits() >> 32, 8) +
            digits(uuid.getMostSignificantBits() >> 16, 4) +
            digits(uuid.getMostSignificantBits(), 4) +
            digits(uuid.getLeastSignificantBits() >> 48, 4) +
            digits(uuid.getLeastSignificantBits(), 12));
}

/** Returns val represented by the specified number of hex digits. */
private String digits(long val, int digits) {
    long hi = 1L << (digits * 4);
    return Long.toHexString(hi | (val & (hi - 1))).substring(1);
}

Uso:

generateUUIDString(UUID.randomUUID())

Otra implementación usando reflexión

public String generateString(UUID uuid) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

    if (uuid == null) {
        return "";
    }

    Method digits = UUID.class.getDeclaredMethod("digits", long.class, int.class);
    digits.setAccessible(true);

    return ( (String) digits.invoke(uuid, uuid.getMostSignificantBits() >> 32, 8) +
            digits.invoke(uuid, uuid.getMostSignificantBits() >> 16, 4) +
            digits.invoke(uuid, uuid.getMostSignificantBits(), 4) +
            digits.invoke(uuid, uuid.getLeastSignificantBits() >> 48, 4) +
            digits.invoke(uuid, uuid.getLeastSignificantBits(), 12));

}
Ravi Desai
fuente
2

Uso org.apache.commons.codec.binary.Base64 para convertir un UUID en una cadena única segura de URL que tiene 22 caracteres de longitud y tiene la misma unicidad que UUID.

Publiqué mi código en Almacenamiento de UUID como cadena base64

stikkos
fuente