Método eficiente para gerar UUID String em JAVA (UUID.randomUUID (). ToString () sem os traços)

154

Eu gostaria de um utilitário eficiente para gerar seqüências únicas de bytes. O UUID é um bom candidato, mas UUID.randomUUID().toString()gera coisas como o 44e128a5-ac7a-4c9a-be4c-224b6bf81b20que é bom, mas eu preferiria uma string sem traços.

Eu estou procurando uma maneira eficiente de gerar seqüências aleatórias, apenas a partir de caracteres alfanuméricos (sem traços ou outros símbolos especiais).

Maxim Veksler
fonte
38
Por que os traços precisam ser removidos para que um UUID seja transmitido por HTTP?
Bruno
6
Eu não acho que traços precisavam ser removidos no HTTP em geral ... qual bit está causando problemas?
precisa saber é o seguinte
2
Talvez em um ambiente móvel, se você ainda pagar por cada byte transmitido, e usando uma rede de baixa largura de banda e alta latência, poupando 4 bytes ainda é importante em alguns cenários ...
Guido
2
Eu quero que os traços sejam removidos porque, mais tarde, usando a string UUID como identificador de solicitação exclusivo, é muito mais fácil trabalhar apenas com caracteres decimais hexadecimais do que [a-f0-9-].
precisa saber é o seguinte
Eu removi a parte HTTP porque não é relevante (como explicou Maxim), apenas confunde os leitores (como pode ser visto nos comentários e nas respostas).
Ondra Žižka

Respostas:

274

Faz isso:

public static void main(String[] args) {
    final String uuid = UUID.randomUUID().toString().replace("-", "");
    System.out.println("uuid = " + uuid);
}
Steve McLeod
fonte
Por exemplo, o Mongodb não usa traços no ObjectID. Portanto, remover traços pode ser útil para a API.
Alexey Ryazhskikh 28/03
1
Vou te dar uma razão do porquê. Existe uma API com a qual estou trabalhando (de alto perfil, bem conhecida) que não permite traços em seu UUID. Você tem que tirá-los.
Michael Gaines
19
Não há necessidade de substituirAll, que usa expressões regulares. Basta fazer .replace ("-", "")
Craigo 31/05
1
substituir método da classe String é um pouco lento, eu acho
bmscomp
@bmscomp para a primeira chamada, é lento, mas para as próximas chamadas, não há problema.
gaurav
30

Os traços não precisam ser removidos da solicitação HTTP, como você pode ver no URL deste segmento. Mas se você deseja preparar uma URL bem formada sem depender dos dados, deve usar URLEncoder.encode (dados da string, codificação da string) em vez de alterar a forma padrão dos dados. Para traços de representação de seqüência de caracteres UUID, é normal.

Donz
fonte
"Os traços não precisam ser removidos da solicitação HTTP, como você pode ver na URL deste segmento." Você não entende, a menos que o Stack Overflow tenha usado UUIDs anteriormente em seus URLs?
precisa saber é o seguinte
1
Não que a url é um UUID, mas que tem traços:http://stackoverflow.com/questions/3804591/efficient-method-to-generate-uuid-string-in-java-uuid-randomuuid-tostring-w?rq=1
Octavia Togami
12

Acabei escrevendo algo próprio com base na implementação do UUID.java. Observe que não estou gerando um UUID , apenas uma sequência hexadecimal aleatória de 32 bytes da maneira mais eficiente possível.

Implementação

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

Testes

Algumas das entradas que testei para garantir que estão funcionando:

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
fonte
1
sem saber por que isso é mais votado, isso gerou UUID sem o "-" no método mais eficiente de todas as opções escritas aqui. A substituição de string não é melhor do que a conversão de long para string. É verdade que ambos são O (n), mas em uma escala em que você gera milhões de uuids por minuto, isso se torna significativo.
Maxim Veksler
10

Eu usei o JUG (Java UUID Generator) para gerar um ID exclusivo. É único nas JVMs. Muito bom de usar. Aqui está o código para sua referência:

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

É possível baixar a biblioteca em: https://github.com/cowtowncoder/java-uuid-generator

Sheng Chien
fonte
Para o seu caso, o que há de errado com UUID.randomUUID (). ToString ()? Observe também que você (teoricamente) diminui a entropia mantendo um SecureRandom final estático (torna-o volátil). também por que sincronizar o generateUniqueId? Isso significa que todos os seus threads estão bloqueados nesse método.
precisa saber é o seguinte
Antes de tudo, Safehaus afirma que o JUG é mais rápido. E pode gerar IDs únicos em máquinas que você pode não precisar. Eles têm método baseado no tempo, que é o mais gordo entre todos os métodos. Sim, a sincronização não é necessária aqui porque eu percebi que o SecureRandom já é seguro para threads. Por que declarar final estático no SecureRandom diminuiria a entropia? Estou curioso :) Há mais detalhes aqui: jug.safehaus.org/FAQ
Sheng Chien
O JUG também pode gerar UUIDs baseados em números aleatórios; mas as principais razões pelas quais os desenvolvedores preferem usar a variação baseada no tempo são 10 a 20 vezes mais rápidas ( cowtowncoder.com/blog/archives/2010/10/entry_429.html ); ou que eles não confiam aleatoriedade para produzir ids únicos (que é meio engraçado)
StaxMan
não jug.safehaus.org não existe mais, mas você pode encontrar o FAQ no raw.github.com/cowtowncoder/java-uuid-generator/3.0/...
Daniel Serodio
+1 por mencionar o JUG - revi sua utilidade, mas é bom saber que existem algumas java.util.UUIDalternativas sérias .
Greg Dubicki
8

Uma solução simples é

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

(Como as soluções existentes, apenas que evita a chamada String # replaceAll . A substituição da expressão regular não é necessária aqui, portanto, a String # replace parece mais natural, embora tecnicamente ainda seja implementada com expressões regulares. Dado que a geração do UUID é mais caro que a substituição, não deve haver uma diferença significativa no tempo de execução.)

O uso da classe UUID provavelmente é rápido o suficiente para a maioria dos cenários, embora eu esperasse que alguma variante escrita à mão especializada, que não precisa do pós-processamento, fosse mais rápida. De qualquer forma, o gargalo do cálculo geral será normalmente o gerador de números aleatórios. No caso da classe UUID, ele usa SecureRandom .

Qual gerador de números aleatórios a ser usado também é uma troca que depende do aplicativo. Se for sensível à segurança, o SecureRandom é, em geral, a recomendação. Caso contrário, o ThreadLocalRandom é uma alternativa (mais rápido que o SecureRandom ou o antigo Random , mas não criptograficamente seguro).

Philipp Claßen
fonte
7

Estou surpreso ao ver tantas seqüências de caracteres substituindo idéias de UUID. Que tal agora:

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

Essa é a maneira mais rápida de fazer isso, pois todo o toString () do UUID já é mais caro, sem mencionar a expressão regular que deve ser analisada e executada ou a substituição por uma string vazia.

Stephan
fonte
6
Isso não é confiável. A saída será mais curto se líderes bits são 0.
OG Cara
7
String.format("0x%016x%016x", f.getMostSignificantBits(), f.getLeastSignificantBits())
galets
@ galets Embora eu tenha votado no seu comentário para resolver o problema com os 0s iniciais, pergunto-me se isso seria melhor comparado à alternativa de substituir traços usando replace.
Igorcadelima
3

Acabei de copiar o método UUID toString () e atualizei-o para remover "-". Será muito mais rápido e direto do que qualquer outra solução

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

Outra implementação usando reflexão

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

Eu uso org.apache.commons.codec.binary.Base64 para converter um UUID em uma string exclusiva segura para URLs com 22 caracteres e com a mesma exclusividade que o UUID.

Publiquei meu código em Armazenando UUID como base64 String

stikkos
fonte