Metodo efficiente per generare la stringa UUID in JAVA (UUID.randomUUID (). ToString () senza i trattini)


154

Vorrei un'utilità efficiente per generare sequenze uniche di byte. UUID è un buon candidato ma UUID.randomUUID().toString()genera cose del genere 44e128a5-ac7a-4c9a-be4c-224b6bf81b20, ma preferirei una stringa senza trattini.

Sto cercando un modo efficiente per generare stringhe casuali, solo da caratteri alfanumerici (senza trattini o altri simboli speciali).


38
Perché i trattini devono essere rimossi per poter trasmettere un tale UUID su HTTP?
Bruno,

6
Non pensavo che i trattini dovessero essere rimossi in HTTP in generale ... quale bit ti sta causando fastidio?
Jon Skeet,

2
Forse in un ambiente mobile, se si paga ancora per ogni byte trasmesso e si utilizza una rete a larghezza di banda ridotta e latenza elevata, il salvataggio di 4 byte è ancora importante in alcuni scenari ...
Guido

2
Voglio che i trattini vengano rimossi perché in seguito utilizzeremo la stringa UUID come identificatore di richiesta univoco, è molto più semplice lavorare con solo caratteri decimali esadecimali quindi [a-f0-9-].
Maxim Veksler,

Ho rimosso la parte HTTP perché non è pertinente (come spiegato da Maxim), confonde solo i lettori (come si può vedere sia nei commenti che nelle risposte).
Ondra Žižka,

Risposte:


274

Questo lo fa:

public static void main(String[] args) {
    final String uuid = UUID.randomUUID().toString().replace("-", "");
    System.out.println("uuid = " + uuid);
}

Ad esempio, Mongodb non utilizza trattini in ObjectID. Quindi la rimozione dei trattini può essere utile per api.
Alexey Ryazhskikh,

1
Ti darò un motivo per cui. C'è un'API con cui sto lavorando (alto profilo, ben noto) che non consente trattini nel suo UUID. Devi spogliarli.
Michael Gaines,

19
Non c'è bisogno di sostituire tutto, che utilizza espressioni regolari. Just do .replace ("-", "")
Craigo

1
Credo che il metodo di sostituzione della classe String sia un po 'lento, credo
bmscomp

@bmscomp per la prima invocazione, è lento, ma per le successive invocazioni non ci sono problemi.
gaurav,

30

I trattini non devono essere rimossi dalla richiesta HTTP come puoi vedere nell'URL di questo thread. Ma se vuoi preparare un URL ben formato senza dipendenza dai dati, dovresti usare URLEncoder.encode (String data, String encoding) invece di cambiare la forma standard dei tuoi dati. Per UUID, i trattini con rappresentazione di stringhe sono normali.


"I trattini non devono essere rimossi dalla richiesta HTTP come puoi vedere nell'URL di questo thread." Non capisco, a meno che Stack Overflow non abbia precedentemente utilizzato UUID nei loro URL?
RenniePet,

1
Non che l'URL sia un UUID, ma che abbia trattini:http://stackoverflow.com/questions/3804591/efficient-method-to-generate-uuid-string-in-java-uuid-randomuuid-tostring-w?rq=1
Octavia Togami,

12

Ho finito per scrivere qualcosa di mio basato sull'implementazione di UUID.java. Nota che non sto generando un UUID , ma solo una stringa esadecimale casuale di 32 byte nel modo più efficiente a cui potrei pensare.

Implementazione

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

test

Alcuni degli input che ho testato per assicurarsi che funzioni:

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

1
non sono sicuro del motivo per cui questo è più votato, questo UUID generato senza il "-" nel metodo più efficiente da tutte le opzioni scritte qui. La sostituzione della stringa non è migliore della conversione da lunga a stringa. È vero che entrambi sono O (n), ma su scala in cui si generano milioni di uuid al minuto diventa significativo.
Maxim Veksler

10

Ho usato JUG (Java UUID Generator) per generare un ID univoco. È unico tra le JVM. Abbastanza buono da usare. Ecco il codice per il tuo riferimento:

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

È possibile scaricare la libreria da: https://github.com/cowtowncoder/java-uuid-generator


Nel tuo caso cosa c'è che non va in UUID.randomUUID (). ToString ()? Nota anche che (teoricamente) riduci l'entropia tenendo un SecureRandom finale statico (rendilo volatile). anche perché sincronizzare generateUniqueId? Questo significa che tutti i tuoi thread sono bloccati con questo metodo.
Maxim Veksler,

Prima di tutto, Safehaus afferma che JUG è più veloce. E può generare ID univoci su macchine che potrebbero non essere necessarie. Hanno un metodo basato sul tempo che è il più grasso tra tutti i metodi. Sì, la sincronizzazione non è necessaria qui perché ho capito che SecureRandom è già sicuro per i thread. Perché dichiarare il finale statico su SecureRandom ridurrebbe l'entropia? Sono curioso :) Ci sono maggiori dettagli qui: jug.safehaus.org/FAQ
Sheng Chien,

JUG può generare anche UUID basati su numeri casuali; ma il motivo principale per cui gli sviluppatori preferiscono utilizzare la variante basata sul tempo è che è 10-20 volte più veloce ( cowtowncoder.com/blog/archives/2010/10/entry_429.html ); o che non si fidano della casualità per produrre ID unici (il che è un po 'divertente)
StaxMan

jug.safehaus.org non esiste più, ma puoi trovare le FAQ su raw.github.com/cowtowncoder/java-uuid-generator/3.0/…
Daniel Serodio,

+1 per menzionare JUG - Ho esaminato la sua utilità ma è bene sapere che ci sono alcune java.util.UUIDalternative serie .
Greg Dubicki,

8

Una soluzione semplice è

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

(Come le soluzioni esistenti, solo che evita la chiamata String # replaceAll . Qui non è richiesta la sostituzione di espressioni regolari, quindi String # sostituisce sembra più naturale, sebbene tecnicamente sia ancora implementato con espressioni regolari. Dato che la generazione dell'UUID è più costoso della sostituzione, non dovrebbe esserci una differenza significativa nel tempo di esecuzione.)

L'uso della classe UUID è probabilmente abbastanza veloce per la maggior parte degli scenari, anche se mi aspetterei che alcune varianti specializzate scritte a mano, che non richiedono il postprocessing, siano più veloci. Ad ogni modo, il collo di bottiglia del calcolo complessivo sarà normalmente il generatore di numeri casuali. Nel caso della classe UUID, utilizza SecureRandom .

Quale generatore di numeri casuali utilizzare è anche un compromesso che dipende dall'applicazione. Se è sensibile alla sicurezza, SecureRandom è, in generale, la raccomandazione. Altrimenti, ThreadLocalRandom è un'alternativa (più veloce di SecureRandom o del vecchio Random , ma non crittograficamente sicuro).


7

Sono sorpreso di vedere così tante stringhe sostituire le idee di UUID. Cosa ne pensi di questo:

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

Questo è il modo più veloce di farlo poiché l'intero toString () di UUID è già più costoso per non parlare dell'espressione regolare che deve essere analizzata ed eseguita o la sostituzione con stringa vuota.


6
Questo non è affidabile. L'output sarà più breve se i bit iniziali sono 0.
OG Dude

7
String.format("0x%016x%016x", f.getMostSignificantBits(), f.getLeastSignificantBits())
Galets

@galets Anche se ho votato a favore il tuo commento per aver risolto il problema con gli 0 iniziali, mi chiedo se questo sarebbe meglio rispetto all'alternativa di sostituire i trattini usando replace.
igorcadelima,


3

Ho appena copiato il metodo UUID toString () e l'ho appena aggiornato per rimuovere "-" da esso. Sarà molto più veloce e diretto rispetto a qualsiasi altra soluzione

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

Un'altra implementazione che utilizza la riflessione

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

}

2

Uso org.apache.commons.codec.binary.Base64 per convertire un UUID in una stringa univoca e sicura, lunga 22 caratteri e con la stessa unicità di UUID.

Ho pubblicato il mio codice su Storing UUID come stringa base64


0

Ho appena implementato questa classe di utilità che crea UUID come String con o senza trattini . Sentitevi liberi di usare e condividere. Spero che sia d'aiuto!

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

Questo è l'output:

// 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'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.