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

154

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

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

Максим Векслер
источник
38
Почему необходимо удалить тире, чтобы такой UUID передавался по HTTP?
Бруно
6
Я не думал, что черты должны быть удалены в HTTP вообще ... какой бит вызывает у вас беспокойство?
Джон Скит
2
Возможно, в мобильной среде, если вы по-прежнему платите за каждый передаваемый байт и используете сеть с низкой пропускной способностью и высокой задержкой, сохранение некоторых 4 байтов все еще важно в некоторых сценариях ...
Гвидо,
2
Я хочу, чтобы тире были удалены, потому что мы позже используем строку UUID в качестве уникального идентификатора запроса, гораздо проще работать только с шестнадцатеричными десятичными символами, чем [a-f0-9-].
Максим Векслер
Я удалил часть HTTP, потому что она неактуальна (как объяснил Максим), только сбивает с толку читателей (как видно из комментариев и ответов).
Ондра Жижка

Ответы:

274

Это делает это:

public static void main(String[] args) {
    final String uuid = UUID.randomUUID().toString().replace("-", "");
    System.out.println("uuid = " + uuid);
}
Стив Маклеод
источник
Например, Mongodb не использует тире в ObjectID. Таким образом, удаление штрихов может быть полезным для API.
Алексей Ряжских
1
Я дам вам причину почему. Есть API, с которым я работаю (широко известный, хорошо известный), который не допускает тире в его UUID. Вы должны раздеть их.
Майкл Гейнс
19
Не нужно делать replaceAll, который использует регулярные выражения. Просто сделайте .replace ("-", "")
Крейго
1
Я думаю, что метод замены класса String немного медленный
bmscomp
@bmscomp для первого вызова, он медленный, но для следующих вызовов проблем нет.
Гаурав
30

Черточки не нужно удалять из HTTP-запроса, как вы можете видеть в URL этой темы. Но если вы хотите подготовить правильно сформированный URL без зависимости от данных, вы должны использовать URLEncoder.encode (String data, String encoding) вместо изменения стандартной формы ваших данных. Для строкового представления UUID штрихи нормальны.

Donz
источник
«Черточки не нужно удалять из HTTP-запроса, как вы можете видеть в URL этой темы». Не понимаю, разве Stack Overflow ранее использовал UUID в своих URL?
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 (делая его изменчивым). и зачем синхронизировать generateUniqueId? Это означает, что все ваши темы заблокированы по этому методу.
Максим Векслер
Прежде всего, Safehaus утверждает, что JUG быстрее. И он может генерировать уникальные идентификаторы на разных машинах, которые вам могут не понадобиться. У них есть метод, основанный на времени, который является лучшим из всех методов. Да, синхронизировать здесь не нужно, потому что я понял, что SecureRandom уже поточно-ориентирован. Почему объявление статического финала на SecureRandom уменьшило бы энтропию? Мне любопытно :) Есть более подробная информация здесь: jug.safehaus.org/FAQ
Sheng Chien
JUG также может генерировать UUID на основе случайных чисел; но основные причины, по которым разработчики предпочитают использовать временной вариант, заключаются либо в том, что он в 10-20 раз быстрее ( cowtowncoder.com/blog/archives/2010/10/entry_429.html ); или что они не доверяют случайности для создания уникальных идентификаторов (что довольно забавно)
StaxMan
jug.safehaus.org больше не существует, но вы можете найти ответы на часто
Даниэль Серодио
+1 за упоминание JUG - я рассмотрел его полезность, но приятно знать, что есть несколько серьезных java.util.UUIDальтернатив.
Грег Дубицки
8

Простое решение

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

(Как и в существующих решениях, только то, что он избегает вызова String # replaceAll . Замена регулярных выражений здесь не требуется, поэтому String # replace кажется более естественным, хотя технически он все еще реализован с помощью регулярных выражений. Учитывая, что генерация 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 уже стоит дороже, не говоря уже о регулярном выражении, которое необходимо проанализировать и выполнить, или о замене пустой строкой.

Stephan
источник
6
Это не надежно. Вывод будет короче, если начальные биты равны 0.
OG Dude
7
String.format("0x%016x%016x", f.getMostSignificantBits(), f.getLeastSignificantBits())
Галец
@galets Хотя я и проголосовал за ваш комментарий за решение проблемы с ведущими нулями, мне интересно, будет ли это лучше по сравнению с альтернативой замены тире с помощью replace.
игоркаделима
3

Я только что скопировал метод toString () UUID и просто обновил его, чтобы удалить из него «-». Это будет намного быстрее и понятнее, чем любое другое решение.

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

stikkos
источник
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'
fabiolimace
источник