web-dev-qa-db-fra.com

Génération d'UUID à 8 caractères uniquement

Les bibliothèques UUID génèrent des UUID de 32 caractères.

Je souhaite générer des UUID à 8 caractères uniquement, est-ce possible?

62
M.J.

Ce n'est pas possible car un UUID est un nombre de 16 octets par définition. Mais vous pouvez bien sûr générer des chaînes uniques de 8 caractères (voir les autres réponses).

Veillez également à générer des UUID plus longs et à les sous-chaîne, car certaines parties de l’ID peuvent contenir des octets fixes (par exemple, c’est le cas des UUID MAC, DCE et MD5).

57
Cephalopod

Vous pouvez essayer RandomStringUtilsclasse de Apache.commons :

import org.Apache.commons.lang3.RandomStringUtils;

final int SHORT_ID_LENGTH = 8;

// all possible unicode characters
String shortId = RandomStringUtils.random(SHORT_ID_LENGTH);

N'oubliez pas qu'il contiendra tous les caractères possibles, qui ne sont ni une URL ni une compatibilité humaine.

Alors, découvrez aussi d'autres méthodes:

// HEX: 0-9, a-f. For example: 6587fddb, c0f182c1
shortId = RandomStringUtils.random(8, "0123456789abcdef"); 

// a-z, A-Z. For example: eRkgbzeF, MFcWSksx
shortId = RandomStringUtils.randomAlphabetic(8); 

// 0-9. For example: 76091014, 03771122
shortId = RandomStringUtils.randomNumeric(8); 

// a-z, A-Z, 0-9. For example: WRMcpIk7, s57JwCVA
shortId = RandomStringUtils.randomAlphanumeric(8); 

Comme d'autres l'ont dit, la probabilité d'une collision entre identifiants et identificateurs plus petits peut être significative. Découvrez comment problème d'anniversaire s'applique à votre cas. Vous pouvez trouver une explication de Nice sur la façon de calculer une approximation dans cette réponse .

31
Anton Purin

Premièrement: même les identifiants uniques générés par Java UUID.randomUUID ou .net GUID ne sont pas uniques à 100%. Especialy UUID.randomUUID est "uniquement" 128 Ainsi, si vous la réduisez à 64 bits, 32 bits, 16 bits (ou même 1 bit), elle devient simplement moins unique.

Il s’agit donc au moins d’une décision basée sur le risque, combien de temps votre uuid doit être.

Deuxièmement: je suppose que lorsque vous parlez de "seulement 8 caractères", vous entendez une chaîne de 8 caractères imprimables normaux.

Si vous voulez une chaîne unique avec une longueur de 8 caractères imprimables, vous pouvez utiliser un encodage base64. Cela signifie 6 bits par caractère, donc vous obtenez 48 bits au total (possible pas très unique - mais peut-être que c'est ok pour votre application)

Le chemin est donc simple: créer un tableau aléatoire de 6 octets

 SecureRandom Rand;
 // ...
 byte[] randomBytes = new byte[16];
 Rand.nextBytes(randomBytes);

Puis transformez-le en chaîne Base64, par exemple en org.Apache.commons.codec.binary.Base64

BTW: cela dépend de votre application s'il existe un meilleur moyen de créer "uuid" que par hasard. (Si vous créez un seul UUID par seconde, il est judicieux d’ajouter un horodatage) (Au fait: si vous combinez (xor) deux valeurs aléatoires, le résultat est toujours au moins aussi aléatoire que le plus au hasard des deux).

15
Ralph

Comme @Cephalopod l'a déclaré, ce n'est pas possible mais vous pouvez raccourcir un UUID à 22 caractères

public static String encodeUUIDBase64(UUID uuid) {
        ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
        bb.putLong(uuid.getMostSignificantBits());
        bb.putLong(uuid.getLeastSignificantBits());
        return StringUtils.trimTrailingCharacter(BaseEncoding.base64Url().encode(bb.array()), '=');
}
6
bstick12

C’est une manière similaire que j’utilise ici pour générer un code d’erreur unique, basé sur la réponse d’Anton Purin, mais reposant sur le plus approprié org.Apache.commons.text.RandomStringGenerator au lieu du (une fois, pas plus) obsolète org.Apache.commons.lang3.RandomStringUtils:

@Singleton
@Component
public class ErrorCodeGenerator implements Supplier<String> {

    private RandomStringGenerator errorCodeGenerator;

    public ErrorCodeGenerator() {
        errorCodeGenerator = new RandomStringGenerator.Builder()
                .withinRange('0', 'z')
                .filteredBy(t -> t >= '0' && t <= '9', t -> t >= 'A' && t <= 'Z', t -> t >= 'a' && t <= 'z')
                .build();
    }

    @Override
    public String get() {
        return errorCodeGenerator.generate(8);
    }

}

Tous les conseils relatifs à la collision sont toujours valables, veuillez en prendre connaissance.

3
BrunoJCM

En fait, je veux un identifiant unique plus court basé sur l’horodatage, j’ai donc essayé le programme ci-dessous.

On peut le deviner avec nanosecond + ( endians.length * endians.length ) combinaisons.

public class TimStampShorterUUID {

    private static final Character [] endians = 
           {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 
            'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 
            'u', 'v', 'w', 'x', 'y', 'z', 
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 
            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 
            'U', 'V', 'W', 'X', 'Y', 'Z',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
            };

   private static ThreadLocal<Character> threadLocal =  new ThreadLocal<Character>();

   private static AtomicLong iterator = new AtomicLong(-1);


    public static String generateShorterTxnId() {
        // Keep this as secure random when we want more secure, in distributed systems
        int firstLetter = ThreadLocalRandom.current().nextInt(0, (endians.length));

        //Sometimes your randomness and timestamp will be same value,
        //when multiple threads are trying at the same nano second
        //time hence to differentiate it, utilize the threads requesting
        //for this value, the possible unique thread numbers == endians.length
        Character secondLetter = threadLocal.get();
        if (secondLetter == null) {
            synchronized (threadLocal) {
                if (secondLetter == null) {
                    threadLocal.set(endians[(int) (iterator.incrementAndGet() % endians.length)]);
                }
            }
            secondLetter = threadLocal.get();
        }
        return "" + endians[firstLetter] + secondLetter + System.nanoTime();
    }


    public static void main(String[] args) {

        Map<String, String> uniqueKeysTestMap = new ConcurrentHashMap<>();

        Thread t1 = new Thread() {  
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }       
        };

        Thread t2 = new Thread() {  
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }       
        };

        Thread t3 = new Thread() {  
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }       
        };

        Thread t4 = new Thread() {  
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }       
        };

        Thread t5 = new Thread() {  
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }
        };

        Thread t6 = new Thread() {  
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }   
        };

        Thread t7 = new Thread() {  
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }
        };

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
        t7.start();
    }
}

UPDATE : Ce code fonctionnera sur une seule machine virtuelle, mais nous devrions penser à une machine virtuelle distribuée. Par conséquent, je pense à deux solutions, une avec DB et l'autre sans DB.

avec DB

Nom de l'entreprise (nom abrégé 3 caractères) ---- Numéro_automne ---- Clé spécifique redis COUNTER
(3 char) --------------------------------------------- ------- (2 caractères) ---------------- (11 caractères)

sans DB

IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ---- Époque en millisecondes
(5 caractères) ----------------- (2char) --------------------- - (2 caractères) ----------------- (6 caractères)

vous mettra à jour une fois le codage terminé.

1
Kanagavelu Sugumar