Ефективний метод для створення рядка UUID в JAVA (UUID.randomUUID (). ToString () без тире)


154

Я хотів би ефективну утиліту для створення унікальних послідовностей байтів. UUID є хорошим кандидатом, але UUID.randomUUID().toString()генерує такі речі, як 44e128a5-ac7a-4c9a-be4c-224b6bf81b20це добре, але я вважаю за краще штрих без штриху.

Я шукаю ефективний спосіб генерування випадкових рядків, лише з буквено-цифрових символів (без тире або будь-яких інших спеціальних символів).


38
Чому тире потрібно видалити, щоб такий UUID передавався через HTTP?
Бруно

6
Я не думав, що тире потрібно взагалі видаляти в HTTP ... який біт викликає у вас клопоту?
Джон Скіт

2
Можливо, у мобільному середовищі, якщо ви все-таки платите за кожен переданий байт і використовуєте мережу з низькою пропускною здатністю та високою затримкою, економлять 4 байти як і раніше важливо в деяких сценаріях ...
Guido

2
Я хочу, щоб тире видаляли, оскільки ми згодом використовуємо рядок UUID як унікальний ідентифікатор запиту, набагато простіше працювати лише з шістнадцятковими десятковими символами тоді [a-f0-9-].
Максим Векслер

Я видалив частину HTTP, оскільки вона не стосується (як пояснив Максим), лише бентежить читачів (як це можна побачити і в коментарях, і у відповідях).
Ondra Žižka

Відповіді:


274

Це робить це:

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

Наприклад, Mongodb не використовує тире в ObjectID. Тож видалення тире може бути корисним для апі.
Олексій Ряжських

1
Я дам тобі причину. Є API, з яким я працюю (високий профіль, добре відомий), який не дозволяє тире в його UUID. Ви повинні їх зняти.
Майкл Гейнс

19
Не потрібно робити замінуAll, яка використовує регулярні вирази. Просто зробіть .замінити ("-", "")
Craigo

1
метод заміни класу String трохи повільний, я думаю
bmscomp

@bmscomp для першого виклику, це повільно, але для наступних викликів проблем немає.
gaurav

30

Тире не потрібно видаляти з HTTP-запиту, як ви бачите в URL-адресі цього потоку. Але якщо ви хочете підготувати добре сформовану URL-адресу без залежності від даних, вам слід використовувати URLEncoder.encode (String data, String encoding) замість зміни стандартної форми ваших даних. Для рядків представлення рядків UUID є нормальним.


"Тире не потрібно видаляти з HTTP-запиту, як ви бачите в URL-адресі цього потоку." Нічого не розумієте, якщо у їхніх URL-адресах раніше не використовувались переповнення стека у використанні UUID?
RenniePet

1
Справа не в тому, що URL-адреса є UUID, а в тому, що він має тире:http://stackoverflow.com/questions/3804591/efficient-method-to-generate-uuid-string-in-java-uuid-randomuuid-tostring-w?rq=1
Октавія Тогамі

12

Закінчила писати щось власне на основі реалізації UUID.java. Зауважте, що я не генерую UUID , а лише випадкову шістнадцяткову шість-шестнадцятковий рядок найефективнішим способом, про який я міг придумати.

Впровадження

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

Використання

RandomUtil.unique()

Тести

Деякі з матеріалів, які я перевірив, щоб переконатися, що він працює:

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
не впевнений, чому це більше застосовується, це створило UUID без "-" в найбільш ефективному методі з усіх варіантів, написаних тут. Заміна рядків не краща, ніж перетворення з довгої в рядок. Це правда, що обидва є O (n), але в масштабі, де ви генеруєте мільйони уйдів за хвилину, це стає сенсом.
Максим Векслер

10

Я використовував JUG (Java UUID Generator) для створення унікального ідентифікатора. Він унікальний у спільних JVM. Дуже добре використовувати. Ось код для вашої довідки:

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

Ви можете завантажити бібліотеку з: https://github.com/cowtowncoder/java-uuid-generator


У вашому випадку що не так з UUID.randomUUID (). ToString ()? Також зауважте, що ви (теоретично) зменшуєте ентропію, тримаючи статичний остаточний SecureRandom (роблячи його мінливим). також чому синхронізувати generatorUniqueId? Це означає, що всі ваші потоки заблоковані цим методом.
Максим Векслер

Перш за все, Safehaus стверджує, що JUG швидше. І він може генерувати унікальні ідентифікатори на машинах, які вам, можливо, не знадобляться. Вони мають метод, заснований на часі, який є найшвидшим серед усіх методів. Так, синхронізація тут не потрібна, тому що я зрозумів, що SecureRandom вже захищений від потоку. Чому оголошення статичного остаточного на SecureRandom зменшить ентропію? Мені цікаво :) Тут є більше деталей: jug.safehaus.org/FAQ
Sheng Chien

JUG також може генерувати UUID на основі випадкових чисел; але основна причина, чому розробники вважають за краще використовувати часовий варіант - це те, що це 10-20 разів швидше ( cowtowncoder.com/blog/archives/2010/10/entry_429.html ); або що вони не довіряють випадковості, щоб створити унікальні ідентифікатори (що
ніколи

jug.safehaus.org більше не існує, але FAQ ви можете знайти на raw.github.com/cowtowncoder/java-uuid-generator/3.0/…
Daniel Serodio

+1 для згадки про JUG - я переглянув його корисність, але добре знати, що є серйозні java.util.UUIDальтернативи.
Грег Дубіцький

8

Просте рішення

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

(Як і в існуючих рішеннях, лише те, що воно уникає виклику String # substituAll . Регулярна заміна виразів тут не потрібна, тому String # замість виглядає природніше, хоча технічно це все ще реалізовано з регулярними виразами. Враховуючи, що генерація UUID є дорожче, ніж заміна, не повинно бути суттєвої різниці в режимі виконання.)

Використання класу UUID, ймовірно, досить швидко для більшості сценаріїв, хоча я би сподівався, що якийсь спеціалізований рукописний варіант, який не потребує післяобробки, буде швидшим. У будь-якому випадку вузьким місцем загальних обчислень зазвичай буде генератор випадкових чисел. У випадку класу UUID він використовує SecureRandom .

Який генератор випадкових чисел використовувати, також є компромісом, який залежить від програми. Якщо він захищений від безпеки, SecureRandom - це загалом рекомендація. Інакше ThreadLocalRandom є альтернативою (швидше, ніж SecureRandom або старий Random , але не криптографічно захищений).


7

Я вражений тим, що стільки рядків замінюють ідеї UUID. Як щодо цього:

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

Це швидкий спосіб зробити це, оскільки весь toString () UUID вже дорожчий, не кажучи вже про регулярний вираз, який необхідно проаналізувати та виконати, або замінити порожнім рядком.


6
Це не є надійним. Вихід буде коротшим, якщо провідні біти дорівнюватимуть 0.
OG Чувак

7
String.format("0x%016x%016x", f.getMostSignificantBits(), f.getLeastSignificantBits())
Галети

@galets Хоча я вже проголосував за ваш коментар щодо вирішення проблеми з ведучими 0, мені цікаво, чи це було б краще порівняно з альтернативою заміни тире, використовуючи replace.
igorcadelima


3

Я щойно скопіював метод UUID toString () і лише оновив його, щоб видалити з нього "-". Це буде набагато швидше і прямо вперед, ніж будь-яке інше рішення

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

Використання:

generateUUIDString(UUID.randomUUID())

Ще одна реалізація з використанням рефлексії

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

Я використовую org.apache.commons.codec.binary.Base64 для перетворення UUID в безпечний для URL унікальний рядок довжиною 22 символи і має ту ж унікальність, що і UUID.

Я розмістив свій код на Зберіганні UUID як base64 String


0

Я щойно реалізував цей клас утиліти, який створює UUID, як String з тиреми або без них . Безкоштовно користуватися та ділитися. Я сподіваюся, що це допомагає!

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

Це вихід:

// 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'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.