Méthode efficace pour générer une chaîne UUID en JAVA (UUID.randomUUID (). ToString () sans les tirets)

154

Je voudrais un utilitaire efficace pour générer des séquences d'octets uniques. UUID est un bon candidat mais UUID.randomUUID().toString()génère des trucs comme 44e128a5-ac7a-4c9a-be4c-224b6bf81b20ce qui est bien, mais je préférerais une chaîne sans tiret.

Je recherche un moyen efficace de générer des chaînes aléatoires, uniquement à partir de caractères alphanumériques (pas de tirets ni d'autres symboles spéciaux).

Maxim Veksler
la source
38
Pourquoi les tirets doivent-ils être supprimés pour qu'un tel UUID soit transmis via HTTP?
Bruno du
6
Je ne pensais pas que les tirets devaient être supprimés dans HTTP en général ... quel bit vous cause des ennuis?
Jon Skeet
2
Peut-être dans un environnement mobile, si vous payez toujours pour chaque octet transmis et que vous utilisez un réseau à faible bande passante et à latence élevée, économiser 4 octets est toujours important dans certains scénarios ...
Guido
2
Je veux que les tirets soient supprimés parce que nous utiliserons plus tard la chaîne UUID comme identificateur de demande unique, il est beaucoup plus facile de travailler avec uniquement des caractères décimaux hexadécimaux, puis [a-f0-9-].
Maxim Veksler
J'ai supprimé la partie HTTP car elle n'est pas pertinente (comme l'explique Maxim), ne fait que confondre les lecteurs (comme on peut le voir à la fois dans les commentaires et les réponses).
Ondra Žižka le

Réponses:

274

Cela le fait:

public static void main(String[] args) {
    final String uuid = UUID.randomUUID().toString().replace("-", "");
    System.out.println("uuid = " + uuid);
}
Steve McLeod
la source
Par exemple, Mongodb n'utilise pas de tirets dans ObjectID. Donc, la suppression des tirets peut être utile pour les API.
Alexey Ryazhskikh
1
Je vais vous expliquer pourquoi. Il y a une API avec laquelle je travaille (de haut niveau, bien connue) qui n'autorise pas les tirets dans son UUID. Vous devez les dépouiller.
Michael Gaines
19
Pas besoin de faire replaceAll, qui utilise des expressions régulières. Just do .replace ("-", "")
Craigo
1
la méthode de remplacement de la classe String est un peu lente, je pense
bmscomp
@bmscomp pour la première invocation, c'est lent, mais pour les prochaines invocations, il n'y a pas de problème.
gaurav
30

Les tirets n'ont pas besoin d'être supprimés de la requête HTTP comme vous pouvez le voir dans l'URL de ce fil. Mais si vous souhaitez préparer une URL bien formée sans dépendre des données, vous devez utiliser URLEncoder.encode (données String, encodage String) au lieu de changer la forme standard de vos données. Pour les tirets de représentation sous forme de chaîne UUID, c'est normal.

Donz
la source
"Les tirets n'ont pas besoin d'être supprimés de la requête HTTP comme vous pouvez le voir dans l'URL de ce fil." Vous ne comprenez pas, à moins que Stack Overflow n'ait déjà utilisé des UUID dans leurs URL?
RenniePet
1
Non pas que l'url soit un UUID, mais qu'elle comporte des tirets:http://stackoverflow.com/questions/3804591/efficient-method-to-generate-uuid-string-in-java-uuid-randomuuid-tostring-w?rq=1
Octavia Togami
12

J'ai fini par écrire quelque chose de moi-même basé sur l'implémentation UUID.java. Notez que je ne génère pas d'UUID , mais simplement une chaîne hexadécimale de 32 octets aléatoire de la manière la plus efficace à laquelle je puisse penser.

la mise en oeuvre

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());
    }       
}

Usage

RandomUtil.unique()

Des tests

Certaines des entrées que j'ai testées pour m'assurer que cela fonctionne:

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
la source
1
Je ne sais pas pourquoi cela est plus voté, cet UUID généré sans le "-" dans la méthode la plus efficace de toutes les options écrites ici. Le remplacement de chaîne n'est pas meilleur que la conversion de longue en chaîne. Il est vrai que les deux sont O (n), mais à une échelle où vous générez des millions d'uuid par minute, cela devient significatif.
Maxim Veksler
10

J'ai utilisé JUG (Java UUID Generator) pour générer un identifiant unique. Il est unique parmi les JVM. Très bon à utiliser. Voici le code pour votre référence:

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();
}

Vous pouvez télécharger la bibliothèque sur: https://github.com/cowtowncoder/java-uuid-generator

Sheng Chien
la source
Pour votre cas, quel est le problème avec UUID.randomUUID (). ToString ()? Notez également que vous diminuez (théoriquement) l'entropie en tenant un SecureRandom final statique (le rendre volatil). aussi pourquoi synchroniser le generateUniqueId? Cela signifie que tous vos threads sont bloqués sur cette méthode.
Maxim Veksler
Tout d'abord, Safehaus affirme que JUG est plus rapide. Et il peut générer des identifiants uniques sur les machines dont vous n'avez peut-être pas besoin. Ils ont une méthode basée sur le temps qui est la plus épaisse parmi toutes les méthodes. Oui, la synchronisation n'est pas nécessaire ici car «j'ai réalisé que SecureRandom est déjà thread-safe. Pourquoi déclarer statique final sur SecureRandom diminuerait-il l'entropie? Je suis curieux :) Il y a plus de détails ici: jug.safehaus.org/FAQ
Sheng Chien
JUG peut également générer des UUID basés sur des nombres aléatoires; mais la principale raison pour laquelle les développeurs préfèrent utiliser une variante basée sur le temps est soit qu'elle est 10 à 20 fois plus rapide ( cowtowncoder.com/blog/archives/2010/10/entry_429.html ); ou qu'ils ne font pas confiance au hasard pour produire des identifiants uniques (ce qui est plutôt drôle)
StaxMan
jug.safehaus.org n'existe plus, mais vous pouvez trouver la FAQ sur raw.github.com/cowtowncoder/java-uuid-generator/3.0
Daniel Serodio
+1 pour mentionner JUG - J'ai examiné son utilité mais il est bon de savoir qu'il existe des java.util.UUIDalternatives sérieuses .
Greg Dubicki
8

Une solution simple est

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

(Comme les solutions existantes, seulement cela évite l' appel String # replaceAll . Le remplacement d'expression régulière n'est pas nécessaire ici, donc String # replace semble plus naturel, bien que techniquement il soit toujours implémenté avec des expressions régulières. Étant donné que la génération de l'UUID est plus coûteux que le remplacement, il ne devrait pas y avoir de différence significative dans l'exécution.)

L'utilisation de la classe UUID est probablement assez rapide pour la plupart des scénarios, même si je m'attendrais à ce que certaines variantes manuscrites spécialisées, qui ne nécessitent pas de post-traitement, soient plus rapides. Quoi qu'il en soit, le goulot d'étranglement du calcul global sera normalement le générateur de nombres aléatoires. Dans le cas de la classe UUID, il utilise SecureRandom .

Le générateur de nombres aléatoires à utiliser est également un compromis qui dépend de l'application. S'il est sensible à la sécurité, SecureRandom est, en général, la recommandation. Sinon, ThreadLocalRandom est une alternative (plus rapide que SecureRandom ou l'ancien Random , mais pas cryptographiquement sécurisé).

Philipp Claßen
la source
7

Je suis étonné de voir autant de chaînes remplacer les idées d'UUID. Que dis-tu de ça:

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

C'est la façon la plus rapide de le faire puisque tout le toString () de l'UUID est déjà plus cher sans parler de l'expression régulière qui doit être analysée et exécutée ou le remplacement par une chaîne vide.

Stéphan
la source
6
Ce n'est pas fiable. La sortie sera plus courte si les premiers bits sont 0.
OG Dude
7
String.format("0x%016x%016x", f.getMostSignificantBits(), f.getLeastSignificantBits())
galets
@galets Bien que j'aie voté votre commentaire pour résoudre le problème avec les 0 en tête, je me demande si ce serait mieux par rapport à l'alternative de remplacer les tirets en utilisant replace.
igorcadelima
3

Je viens de copier la méthode UUID toString () et je l'ai juste mise à jour pour en supprimer "-". Ce sera beaucoup plus rapide et simple que toute autre solution

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);
}

Usage:

generateUUIDString(UUID.randomUUID())

Une autre implémentation utilisant la réflexion

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
la source
2

J'utilise org.apache.commons.codec.binary.Base64 pour convertir un UUID en une chaîne unique sécurisée pour les URL, d'une longueur de 22 caractères et de la même unicité que l'UUID.

J'ai publié mon code sur le stockage de l'UUID en tant que chaîne base64

stikkos
la source
0

Je viens d'implémenter cette classe utilitaire qui crée des UUID sous forme de chaîne avec ou sans tirets . N'hésitez pas à utiliser et à partager. J'espère que ça aide!

package your.package.name;

import java.security.SecureRandom;
import java.util.Random;

/**
 * Utility class that creates random-based UUIDs.
 * 
 */
public abstract class RandomUuidStringCreator {

    private static final int RANDOM_VERSION = 4;

    /**
     * Returns a random-based UUID as String.
     * 
     * It uses a thread local {@link SecureRandom}.
     * 
     * @return a random-based UUID string
     */
    public static String getRandomUuid() {
        return getRandomUuid(SecureRandomLazyHolder.SECURE_RANDOM);
    }

    /**
     * Returns a random-based UUID as String WITH dashes.
     * 
     * It uses a thread local {@link SecureRandom}.
     * 
     * @return a random-based UUID string
     */
    public static String getRandomUuidWithDashes() {
        return format(getRandomUuid());
    }

    /**
     * Returns a random-based UUID String.
     * 
     * It uses any instance of {@link Random}.
     * 
     * @return a random-based UUID string
     */
    public static String getRandomUuid(Random random) {

        long msb = 0;
        long lsb = 0;

        // (3) set all bit randomly
        if (random instanceof SecureRandom) {
            // Faster for instances of SecureRandom
            final byte[] bytes = new byte[16];
            random.nextBytes(bytes);
            msb = toNumber(bytes, 0, 8); // first 8 bytes for MSB
            lsb = toNumber(bytes, 8, 16); // last 8 bytes for LSB
        } else {
            msb = random.nextLong(); // first 8 bytes for MSB
            lsb = random.nextLong(); // last 8 bytes for LSB
        }

        // Apply version and variant bits (required for RFC-4122 compliance)
        msb = (msb & 0xffffffffffff0fffL) | (RANDOM_VERSION & 0x0f) << 12; // apply version bits
        lsb = (lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // apply variant bits

        // Convert MSB and LSB to hexadecimal
        String msbHex = zerofill(Long.toHexString(msb), 16);
        String lsbHex = zerofill(Long.toHexString(lsb), 16);

        // Return the UUID
        return msbHex + lsbHex;
    }

    /**
     * Returns a random-based UUID as String WITH dashes.
     * 
     * It uses a thread local {@link SecureRandom}.
     * 
     * @return a random-based UUID string
     */
    public static String getRandomUuidWithDashes(Random random) {
        return format(getRandomUuid(random));
    }

    private static long toNumber(final byte[] bytes, final int start, final int length) {
        long result = 0;
        for (int i = start; i < length; i++) {
            result = (result << 8) | (bytes[i] & 0xff);
        }
        return result;
    }

    private static String zerofill(String string, int length) {
        return new String(lpad(string.toCharArray(), length, '0'));
    }

    private static char[] lpad(char[] chars, int length, char fill) {

        int delta = 0;
        int limit = 0;

        if (length > chars.length) {
            delta = length - chars.length;
            limit = length;
        } else {
            delta = 0;
            limit = chars.length;
        }

        char[] output = new char[chars.length + delta];
        for (int i = 0; i < limit; i++) {
            if (i < delta) {
                output[i] = fill;
            } else {
                output[i] = chars[i - delta];
            }
        }
        return output;
    }

    private static String format(String string) {
        char[] input = string.toCharArray();
        char[] output = new char[36];

        System.arraycopy(input, 0, output, 0, 8);
        System.arraycopy(input, 8, output, 9, 4);
        System.arraycopy(input, 12, output, 14, 4);
        System.arraycopy(input, 16, output, 19, 4);
        System.arraycopy(input, 20, output, 24, 12);

        output[8] = '-';
        output[13] = '-';
        output[18] = '-';
        output[23] = '-';

        return new String(output);
    }

    // Holds lazy secure random
    private static class SecureRandomLazyHolder {
        static final Random SECURE_RANDOM = new SecureRandom();
    }

    /**
     * For tests!
     */
    public static void main(String[] args) {

        System.out.println("// Using `java.security.SecureRandom` (DEFAULT)");
        System.out.println("RandomUuidCreator.getRandomUuid()");
        System.out.println();
        for (int i = 0; i < 5; i++) {
            System.out.println(RandomUuidStringCreator.getRandomUuid());
        }

        System.out.println();
        System.out.println("// Using `java.util.Random` (FASTER)");
        System.out.println("RandomUuidCreator.getRandomUuid(new Random())");
        System.out.println();
        Random random = new Random();
        for (int i = 0; i < 5; i++) {
            System.out.println(RandomUuidStringCreator.getRandomUuid(random));
        }
    }
}

Voici la sortie:

// Using `java.security.SecureRandom` (DEFAULT)
RandomUuidStringCreator.getRandomUuid()

'f553ca75657b4b5d85bedf1082785a0b'
'525ecc389e934f209b97d0f0db09d9c6'
'93ec6425bb04499ab47b790fd013ab0d'
'c2d438c620ea4cd5baafd448f9fe945b'
'fb4bc5734931415e94e78da62cb5fe0d'

// Using `java.util.Random` (FASTER)
RandomUuidStringCreator.getRandomUuid(new Random())

'051360b5c92d40fbbb89b40842adbacc'
'a993896538aa43faacbcfd83f913f38b'
'720684d22c584d5299cb03cdbc1912d2'
'82cf94ea296a4a138a92825a0068d4a1'
'a7eda46a215c4e55be3aa957ba74ca9c'
fabiolimace
la source