Metode yang efisien untuk menghasilkan String UUID di JAVA (UUID.randomUUID (). ToString () tanpa tanda hubung)

154

Saya ingin utilitas yang efisien untuk menghasilkan urutan byte yang unik. UUID adalah kandidat yang baik tetapi UUID.randomUUID().toString()menghasilkan hal-hal seperti 44e128a5-ac7a-4c9a-be4c-224b6bf81b20yang baik, tetapi saya lebih suka string tanpa putus.

Saya mencari cara yang efisien untuk menghasilkan string acak, hanya dari karakter alfanumerik (tanpa tanda hubung atau simbol khusus lainnya).

Maxim Veksler
sumber
38
Mengapa tanda hubung harus dihapus agar UUID tersebut ditransmisikan melalui HTTP?
Bruno
6
Saya tidak berpikir tanda hubung perlu dihapus dalam HTTP secara umum ... bit mana yang menyebabkan Anda repot?
Jon Skeet
2
Mungkin di lingkungan seluler, jika Anda masih membayar untuk setiap byte yang ditransmisikan, dan menggunakan jaringan bandwidth rendah & latensi tinggi, menghemat 4 byte masih penting dalam beberapa skenario ...
Guido
2
Saya ingin tanda hubung dihapus karena kita nanti menggunakan string UUID sebagai pengidentifikasi permintaan unik, itu jauh lebih mudah bekerja dengan hanya karakter hex desimal kemudian [a-f0-9-].
Maxim Veksler
Saya telah menghapus bagian HTTP karena tidak relevan (seperti yang dijelaskan Maxim), hanya membingungkan para pembaca (seperti dapat dilihat baik di komentar maupun jawaban).
Ondra Žižka

Jawaban:

274

Ini melakukannya:

public static void main(String[] args) {
    final String uuid = UUID.randomUUID().toString().replace("-", "");
    System.out.println("uuid = " + uuid);
}
Steve McLeod
sumber
Sebagai contoh, Mongodb tidak menggunakan tanda hubung di ObjectID. Jadi menghapus garis dapat berguna untuk api.
Alexey Ryazhskikh
1
Saya akan memberi Anda alasan mengapa. Ada API yang saya kerjakan (profil tinggi, terkenal) yang tidak memungkinkan tanda hubung dalam UUID-nya. Anda harus menelanjangi mereka.
Michael Gaines
19
Tidak perlu melakukan replaceAll, yang menggunakan ekspresi reguler. Lakukan saja .replace ("-", "")
Craigo
1
ganti metode kelas String agak lambat, saya pikir
bmscomp
@ bmscomp untuk doa pertama, itu lambat, tetapi untuk doa berikutnya, tidak ada masalah.
gaurav
30

Tanda hubung tidak perlu dihapus dari permintaan HTTP seperti yang Anda lihat di URL utas ini. Tetapi jika Anda ingin menyiapkan URL yang terbentuk dengan baik tanpa bergantung pada data, Anda harus menggunakan URLEncoder.encode (Data string, Pengkodean string) alih-alih mengubah bentuk standar data Anda. Untuk garis representasi string UUID adalah normal.

Donz
sumber
"Tanda hubung tidak perlu dihapus dari permintaan HTTP seperti yang Anda lihat di URL utas ini." Tidak mengerti, kecuali Stack Overflow sebelumnya menggunakan UUID di URL mereka?
RenniePet
1
Bukan berarti url adalah UUID, tetapi memiliki garis http://stackoverflow.com/questions/3804591/efficient-method-to-generate-uuid-string-in-java-uuid-randomuuid-tostring-w?rq=1
putus-putus
12

Akhirnya menulis sesuatu sendiri berdasarkan implementasi UUID.java. Perhatikan bahwa saya tidak membuat UUID , melainkan hanya string hex 32 byte acak dengan cara yang paling efisien yang dapat saya pikirkan.

Penerapan

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

Pemakaian

RandomUtil.unique()

Tes

Beberapa input yang saya uji untuk memastikan itu berfungsi:

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
sumber
1
tidak yakin mengapa ini lebih ditingkatkan, UUID ini dihasilkan tanpa "-" dalam metode paling efisien dari semua opsi yang ditulis di sini. Penggantian string tidak lebih baik daripada konversi dari panjang ke string. Memang benar bahwa keduanya adalah O (n), namun pada skala di mana Anda menghasilkan jutaan uuid satu menit itu menjadi bermakna.
Maxim Veksler
10

Saya menggunakan JUG (Java UUID Generator) untuk menghasilkan ID unik. Ini unik di seluruh JVM. Cukup bagus untuk digunakan. Berikut ini kode untuk referensi Anda:

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

Anda dapat mengunduh perpustakaan dari: https://github.com/cowtowncoder/java-uuid-generator

Sheng Chien
sumber
Untuk kasus Anda, apa yang salah dengan UUID.randomUUID (). ToString ()? Juga perhatikan bahwa Anda (secara teoritis) mengurangi entropi dengan memegang SecureRandom akhir statis (membuatnya volatile). juga mengapa menyinkronkan generateUniqueId? Ini berarti semua utas Anda diblokir pada metode ini.
Maxim Veksler
Pertama-tama, Safehaus mengklaim JUG lebih cepat. Dan itu dapat menghasilkan ID unik di mesin yang mungkin tidak Anda butuhkan. Mereka memiliki metode berbasis waktu yang merupakan metode yang paling gemuk di antara semua metode lainnya. Ya, disinkronkan tidak diperlukan di sini karena 'Saya menyadari SecureRandom sudah aman utas. Mengapa menyatakan final statis pada SecureRandom akan mengurangi entropi? Saya ingin tahu :) Ada detail lebih lanjut di sini: jug.safehaus.org/FAQ
Sheng Chien
JUG juga dapat menghasilkan UUID berbasis angka acak; tetapi alasan utama mengapa pengembang lebih suka menggunakan varian berbasis waktu adalah karena 10-20x lebih cepat ( cowtowncoder.com/blog/archives/2010/10/entry_429.html ); atau bahwa mereka tidak mempercayai keacakan untuk menghasilkan id unik (yang agak lucu)
StaxMan
jug.safehaus.org tidak ada lagi, tetapi Anda dapat menemukan FAQ di raw.github.com/cowtowncoder/java-uuid-generator/3.0/…
Daniel Serodio
+1 untuk menyebutkan JUG - Saya telah meninjau kegunaannya tetapi ada baiknya mengetahui bahwa ada beberapa java.util.UUIDalternatif serius .
Greg Dubicki
8

Solusi sederhana adalah

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

(Seperti solusi yang ada, hanya saja ia menghindari panggilan String # replaceAll . Penggantian ekspresi reguler tidak diperlukan di sini, jadi String # replace terasa lebih alami, meskipun secara teknis masih diimplementasikan dengan ekspresi reguler. Mengingat bahwa generasi UUID adalah lebih mahal daripada penggantian, seharusnya tidak ada perbedaan signifikan dalam runtime.)

Menggunakan kelas UUID mungkin cukup cepat untuk sebagian besar skenario, meskipun saya berharap bahwa beberapa varian tulisan tangan khusus, yang tidak memerlukan postprocessing, menjadi lebih cepat. Bagaimanapun, hambatan dari keseluruhan perhitungan biasanya akan menjadi generator angka acak. Dalam kasus kelas UUID, ia menggunakan SecureRandom .

Generator nomor acak mana yang digunakan juga merupakan trade-off yang tergantung pada aplikasi. Jika sensitif terhadap keamanan, SecureRandom pada umumnya adalah rekomendasi. Jika tidak, ThreadLocalRandom adalah alternatif (lebih cepat dari SecureRandom atau Acak lama , tetapi tidak aman secara kriptografis).

Philipp Claßen
sumber
7

Saya kagum melihat begitu banyak benang menggantikan gagasan UUID. Bagaimana dengan ini:

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

Ini adalah cara berpuasa melakukannya karena seluruh toString () UUID sudah lebih mahal belum lagi ekspresi reguler yang harus diurai dan dieksekusi atau diganti dengan string kosong.

Stephan
sumber
6
Ini tidak bisa diandalkan. Output akan lebih pendek jika bit terkemuka adalah 0.
OG Dude
7
String.format("0x%016x%016x", f.getMostSignificantBits(), f.getLeastSignificantBits())
Galet
@galets Meskipun saya sudah memilih komentar Anda untuk memecahkan masalah dengan memimpin 0s, saya bertanya-tanya apakah ini akan lebih baik dibandingkan dengan alternatif mengganti menggunakan tanda hubung replace.
igorcadelima
3

Saya baru saja menyalin metode UUID toString () dan baru saja memperbaruinya untuk menghapus "-" darinya. Ini akan jauh lebih cepat dan lurus ke depan daripada solusi lainnya

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

Pemakaian:

generateUUIDString(UUID.randomUUID())

Implementasi lain menggunakan refleksi

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

Saya menggunakan org.apache.commons.codec.binary.Base64 untuk mengubah UUID menjadi string unik url-safe yang panjangnya 22 karakter dan memiliki keunikan yang sama dengan UUID.

Saya memposting kode saya pada Menyimpan UUID sebagai string base64

stikkos
sumber
0

Saya baru saja mengimplementasikan kelas utilitas ini yang membuat UUID sebagai String dengan atau tanpa tanda hubung . Jatuh bebas untuk digunakan dan berbagi. Saya harap ini membantu!

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

Ini hasilnya:

// 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
sumber