Effiziente Methode zum Generieren eines UUID-Strings in JAVA (UUID.randomUUID (). ToString () ohne Bindestriche)

154

Ich möchte ein effizientes Dienstprogramm, um eindeutige Folgen von Bytes zu generieren. UUID ist ein guter Kandidat, UUID.randomUUID().toString()generiert aber Dinge, 44e128a5-ac7a-4c9a-be4c-224b6bf81b20die gut sind, aber ich würde eine strichlose Zeichenfolge bevorzugen.

Ich suche nach einer effizienten Möglichkeit, zufällige Zeichenfolgen nur aus alphanumerischen Zeichen (keine Bindestriche oder andere spezielle Symbole) zu generieren.

Maxim Veksler
quelle
38
Warum müssen die Bindestriche entfernt werden, damit eine solche UUID über HTTP übertragen werden kann?
Bruno
6
Ich dachte nicht, dass Bindestriche in HTTP im Allgemeinen entfernt werden müssen ... welches Bit verursacht Ärger?
Jon Skeet
2
Vielleicht ist es in einer mobilen Umgebung in einigen Szenarien immer noch wichtig, 4 Bytes zu sparen, wenn Sie immer noch für jedes übertragene Byte bezahlen und ein Netzwerk mit geringer Bandbreite und hoher Latenz verwenden ...
Guido
2
Ich möchte, dass die Bindestriche entfernt werden, da wir später die UUID-Zeichenfolge als eindeutige Anforderungskennung verwenden. Es ist viel einfacher, nur mit hexadezimalen Dezimalzeichen zu arbeiten als mit [a-f0-9-].
Maxim Veksler
Ich habe den HTTP-Teil entfernt, weil er nicht relevant ist (wie Maxim erklärt hat), sondern nur die Leser verwirrt (wie sowohl in Kommentaren als auch in Antworten zu sehen ist).
Ondra Žižka

Antworten:

274

Das macht es:

public static void main(String[] args) {
    final String uuid = UUID.randomUUID().toString().replace("-", "");
    System.out.println("uuid = " + uuid);
}
Steve McLeod
quelle
Beispielsweise verwendet Mongodb in ObjectID keine Bindestriche. Das Entfernen von Bindestrichen kann daher für die API nützlich sein.
Alexey Ryazhskikh
1
Ich gebe dir einen Grund warum. Es gibt eine API, mit der ich arbeite (hochkarätig, bekannt), die keine Bindestriche in ihrer UUID zulässt. Du musst sie ausziehen.
Michael Gaines
19
Es ist nicht erforderlich, replaceAll auszuführen, da reguläre Ausdrücke verwendet werden. Ersetzen Sie einfach ("-", "")
Craigo
1
Ersetzungsmethode der String-Klasse ist ein bisschen langsam, denke ich
bmscomp
@bmscomp für den ersten Aufruf ist es langsam, aber für die nächsten Aufrufe gibt es kein Problem.
Gaurav
30

Bindestriche müssen nicht aus der HTTP-Anforderung entfernt werden, wie Sie in der URL dieses Threads sehen können. Wenn Sie jedoch eine wohlgeformte URL ohne Abhängigkeit von Daten erstellen möchten, sollten Sie URLEncoder.encode (String-Daten, String-Codierung) verwenden, anstatt die Standardform Ihrer Daten zu ändern. Für die Darstellung von UUID-Zeichenfolgen sind Striche normal.

Donz
quelle
"Bindestriche müssen nicht aus der HTTP-Anforderung entfernt werden, wie Sie in der URL dieses Threads sehen können." Nicht verstehen, es sei denn, Stack Overflow hat zuvor UUIDs in ihren URLs verwendet?
RenniePet
1
Nicht dass die URL eine UUID ist, aber dass sie Bindestriche enthält:http://stackoverflow.com/questions/3804591/efficient-method-to-generate-uuid-string-in-java-uuid-randomuuid-tostring-w?rq=1
Octavia Togami
12

Am Ende habe ich etwas Eigenes geschrieben, das auf der Implementierung von UUID.java basiert. Beachten Sie, dass ich keine UUID generiere , sondern nur eine zufällige 32-Byte-Hex-Zeichenfolge auf die effizienteste Art und Weise, die ich mir vorstellen kann.

Implementierung

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

Verwendung

RandomUtil.unique()

Tests

Einige der Eingaben, die ich getestet habe, um sicherzustellen, dass sie funktionieren:

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
quelle
1
nicht sicher, warum dies mehr positiv bewertet wird, dies erzeugte UUID ohne das "-" in der effizientesten Methode aus allen hier geschriebenen Optionen. Das Ersetzen von Strings ist nicht besser als die Konvertierung von Long in String. Es ist wahr, dass beide O (n) sind, aber in einem Maßstab, in dem Sie Millionen von UUIDs pro Minute generieren, wird dies bedeutungsvoll.
Maxim Veksler
10

Ich habe JUG (Java UUID Generator) verwendet, um eine eindeutige ID zu generieren. Es ist einzigartig für JVMs. Ziemlich gut zu bedienen. Hier ist der Code als Referenz:

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

Sie können die Bibliothek unter folgender Adresse herunterladen: https://github.com/cowtowncoder/java-uuid-generator

Sheng Chien
quelle
Was ist in Ihrem Fall mit UUID.randomUUID (). ToString () falsch? Beachten Sie auch, dass Sie (theoretisch) die Entropie verringern, indem Sie ein statisches endgültiges SecureRandom halten (es flüchtig machen). auch warum die generateUniqueId synchronisieren? Dies bedeutet, dass alle Ihre Threads bei dieser Methode blockiert sind.
Maxim Veksler
Zunächst behauptet Safehaus, JUG sei schneller. Außerdem können auf allen Computern eindeutige IDs generiert werden, die Sie möglicherweise nicht benötigen. Sie haben eine zeitbasierte Methode, die die fetteste unter allen Methoden ist. Ja, eine Synchronisierung ist hier nicht erforderlich, da ich festgestellt habe, dass SecureRandom bereits threadsicher ist. Warum würde das Deklarieren des statischen Finales in SecureRandom die Entropie verringern? Ich bin neugierig :) Es gibt mehr Details hier: jug.safehaus.org/FAQ
Sheng Chien
JUG kann auch UUIDs auf der Basis von Zufallszahlen generieren. Die Hauptgründe, warum Entwickler die zeitbasierte Variante bevorzugen, sind entweder, dass sie 10 bis 20 Mal schneller ist ( cowtowncoder.com/blog/archives/2010/10/entry_429.html ); oder dass sie der Zufälligkeit nicht vertrauen, um eindeutige IDs zu erstellen (was irgendwie lustig ist)
StaxMan
jug.safehaus.org existiert nicht mehr, aber Sie finden die FAQ unter raw.github.com/cowtowncoder/java-uuid-generator/3.0/…
Daniel Serodio
+1 für die Erwähnung von JUG - Ich habe seine Nützlichkeit überprüft, aber es ist gut zu wissen, dass es einige ernsthafte java.util.UUIDAlternativen gibt.
Greg Dubicki
8

Eine einfache Lösung ist

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

(Wie bei den vorhandenen Lösungen wird nur der Aufruf von String # replaceAll vermieden . Das Ersetzen von regulären Ausdrücken ist hier nicht erforderlich, sodass sich das Ersetzen von String # natürlicher anfühlt, obwohl es technisch immer noch mit regulären Ausdrücken implementiert wird. Angesichts der Tatsache, dass die UUID generiert wird teurer als der Austausch, sollte es keinen signifikanten Unterschied in der Laufzeit geben.)

Die Verwendung der UUID-Klasse ist für die meisten Szenarien wahrscheinlich schnell genug, obwohl ich erwarten würde, dass eine spezielle handgeschriebene Variante, für die keine Nachbearbeitung erforderlich ist, schneller ist. Wie auch immer, der Engpass bei der Gesamtberechnung ist normalerweise der Zufallszahlengenerator. Bei der UUID-Klasse wird SecureRandom verwendet .

Welcher Zufallszahlengenerator verwendet werden soll, hängt auch von der Anwendung ab. Wenn es sicherheitsrelevant ist, ist SecureRandom im Allgemeinen die Empfehlung. Andernfalls ist ThreadLocalRandom eine Alternative (schneller als SecureRandom oder das alte Random , jedoch nicht kryptografisch sicher).

Philipp Claßen
quelle
7

Ich bin erstaunt zu sehen, wie viele Zeichenfolgen Ideen von UUID ersetzen. Wie wäre es damit:

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

Dies ist die schnellste Methode, da das gesamte toString () der UUID bereits teurer ist, ganz zu schweigen von dem regulären Ausdruck, der analysiert und ausgeführt werden muss, oder dem Ersetzen durch eine leere Zeichenfolge.

Stephan
quelle
6
Dies ist nicht zuverlässig. Die Ausgabe wird kürzer sein, wenn die führenden Bits 0 sind.
OG Dude
7
String.format("0x%016x%016x", f.getMostSignificantBits(), f.getLeastSignificantBits())
Galets
@galets Obwohl ich für Ihren Kommentar zur Lösung des Problems mit führenden Nullen gestimmt habe, frage ich mich, ob dies besser wäre als die Alternative, Bindestriche durch zu ersetzen replace.
Igorcadelima
3

Ich habe gerade die UUID toString () -Methode kopiert und sie aktualisiert, um "-" daraus zu entfernen. Es wird viel schneller und unkomplizierter sein als jede andere Lösung

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

Verwendung:

generateUUIDString(UUID.randomUUID())

Eine weitere Implementierung mit Reflexion

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
quelle
2

Ich verwende org.apache.commons.codec.binary.Base64, um eine UUID in eine URL-sichere eindeutige Zeichenfolge zu konvertieren, die 22 Zeichen lang ist und dieselbe Eindeutigkeit wie die UUID aufweist.

Ich habe meinen Code beim Speichern der UUID als base64-Zeichenfolge veröffentlicht

Stikkos
quelle