Por supuesto. Pero probablemente no sea tan sencillo y más corto es menos probable que sea realmente único. ¿Entonces por qué?
@delnan, para ser utilizado en un entorno integrado?
Allen Zhang
1
Si la cadena resultante se puede almacenar en UTF-8, potencialmente tiene 4 bytes por carácter. Si puede usar todo ese rango, solo necesitaría 4 caracteres UTF-8 para representar la misma información.
EECOLOR
Respuestas:
72
No es posible porque un UUID es un número de 16 bytes por definición. Pero, por supuesto, puede generar cadenas únicas de 8 caracteres de largo (consulte las otras respuestas).
También tenga cuidado con generar UUID más largos y subcadenas, ya que algunas partes del ID pueden contener bytes fijos (por ejemplo, este es el caso de los UUID MAC, DCE y MD5).
Como dijeron otros, la probabilidad de colisión de identificación con identificación más pequeña puede ser significativa. Vea cómo se aplica el problema del cumpleaños a su caso. Puede encontrar una buena explicación sobre cómo calcular la aproximación en esta respuesta .
Dado que org.apache.commons.lang3.RandomStringUtilsestá en desuso, sería mejor usar org.apache.commons.text.RandomStringGeneratoren commons.apache.org/proper/commons-text
BrunoJCM
Se agregó una nueva respuesta para RandomStringGenerator, ya que es un código bastante diferente.
BrunoJCM
2
Solo un FYI para los futuros espectadores, Randomness no garantiza la singularidad. Los generadores aleatorios garantizan la aleatoriedad; y puede producir un conjunto válido de números aleatorios con valores repetidos.
Vishnu Prasad V
RandomStringUtilsNO está en desuso. Está destinado a un uso sencillo. ¿Puede proporcionar una fuente de información RandomStringUtilsobsoleta? Puedo proporcionar la documentación de la última versión de RandomStringUtilscomo prueba de que no está obsoleta: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…
krm
Solo al verificar un mapa o hashset con uuids ya usados, la probabilidad de colisión es enorme.
Anton
18
Primero: Incluso los ID únicos generados por java UUID.randomUUID o .net GUID no son 100% únicos. Especialmente UUID.randomUUID es "sólo" un valor aleatorio de 128 bits (seguro). Entonces, si lo reduce a 64 bits, 32 bits, 16 bits (o incluso 1 bit), simplemente se vuelve menos único.
Por lo tanto, es al menos una decisión basada en el riesgo, la duración de su uuid.
Segundo: supongo que cuando habla de "solo 8 caracteres" se refiere a una cadena de 8 caracteres imprimibles normales.
Si desea una cadena única con una longitud de 8 caracteres imprimibles, puede usar una codificación base64. Esto significa 6 bits por carácter, por lo que obtiene 48 bits en total (es posible que no sea muy único, pero tal vez esté bien para su aplicación)
Entonces la forma es simple: cree una matriz aleatoria de 6 bytes
Y luego transfórmalo en una Cadena Base64, por ejemplo, org.apache.commons.codec.binary.Base64
Por cierto: depende de su aplicación si hay una mejor manera de crear "uuid" que al azar. (Si crea un UUID solo una vez por segundo, entonces es una buena idea agregar una marca de tiempo) (Por cierto: si combina (xor) dos valores aleatorios, el resultado siempre es al menos tan aleatorio como la mayoría aleatorio de los dos).
Esta es una forma similar que estoy usando aquí para generar un código de error único, basado en la respuesta de Anton Purin, pero confiando en el más apropiado en org.apache.commons.text.RandomStringGeneratorlugar del (una vez, ya no) en desuso org.apache.commons.lang3.RandomStringUtils:
@Singleton@ComponentpublicclassErrorCodeGeneratorimplementsSupplier<String> {
private RandomStringGenerator errorCodeGenerator;
publicErrorCodeGenerator(){
errorCodeGenerator = new RandomStringGenerator.Builder()
.withinRange('0', 'z')
.filteredBy(t -> t >= '0' && t <= '9', t -> t >= 'A' && t <= 'Z', t -> t >= 'a' && t <= 'z')
.build();
}
@Overridepublic String get(){
return errorCodeGenerator.generate(8);
}
}
Todos los consejos sobre colisión siguen siendo válidos, por favor, téngalos en cuenta.
RandomStringUtilsNO está en desuso. Está destinado a un uso sencillo. ¿Puede proporcionar una fuente de información RandomStringUtilsobsoleta? Puedo proporcionar la documentación de la última versión de RandomStringUtilscomo prueba de que no está obsoleta: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…
Gracias por la explicación BrunoJCM. En el momento actual RandomStringUtilsno está en desuso y de acuerdo con las referencias proporcionadas por usted, hay una buena razón para mantenerlo en desuso, porque es mucho más simple de usar que RandomStringGeneratorpara casos de uso simples. ¿Quizás puedas actualizar tu respuesta? Si / cuando RandomStringUtilso su funcionalidad para casos de uso simples se moverá a commons.text, entonces puede actualizar su respuesta nuevamente, pero actualmente es engañoso.
krm
Se agregó una nota, pero nuevamente, está claro que el proyecto Apache Commons está moviendo las utilidades de texto de commons.langa commons.text, no hay ninguna razón para que alguien use la primera en lugar de la última, aparte de que ya la esté usando en otro lugar. La simplicidad aquí es bastante subjetiva, encuentro que mi respuesta sigue siendo muy simple y nunca la cambiaría por algo que requiera la importación de Commons Lang.
BrunoJCM
1
¿Que tal este? En realidad, este código devuelve un máximo de 13 caracteres, pero es más corto que el UUID.
import java.nio.ByteBuffer;
import java.util.UUID;
/**
* Generate short UUID (13 characters)
*
* @return short UUID
*/publicstatic String shortUUID(){
UUID uuid = UUID.randomUUID();
long l = ByteBuffer.wrap(uuid.toString().getBytes()).getLong();
return Long.toString(l, Character.MAX_RADIX);
}
Sabes que getLong()solo está leyendo los primeros 8 bytes del búfer. El UUID tendrá al menos 36 bytes. ¿Me estoy perdiendo algo porque para mí esto nunca funcionaría?
Edwin Dalorzo
2
Los primeros 8 bytes son los bits más significativos de UUID. según esta respuesta, los bits menos significativos son más aleatorios. Así que Long.toString(uuid.getLessSignificantBits(), Character.MAX_RADIX)es mejor.
DouO
0
En realidad, quiero un identificador único más corto basado en la marca de tiempo, por lo tanto, probé el siguiente programa.
Es adivinable con nanosecond + ( endians.length * endians.length )combinaciones.
publicclassTimStampShorterUUID{
privatestaticfinal Character [] endians =
{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};
privatestatic ThreadLocal<Character> threadLocal = new ThreadLocal<Character>();
privatestatic AtomicLong iterator = new AtomicLong(-1);
publicstatic String generateShorterTxnId(){
// Keep this as secure random when we want more secure, in distributed systemsint firstLetter = ThreadLocalRandom.current().nextInt(0, (endians.length));
//Sometimes your randomness and timestamp will be same value,//when multiple threads are trying at the same nano second//time hence to differentiate it, utilize the threads requesting//for this value, the possible unique thread numbers == endians.length
Character secondLetter = threadLocal.get();
if (secondLetter == null) {
synchronized (threadLocal) {
if (secondLetter == null) {
threadLocal.set(endians[(int) (iterator.incrementAndGet() % endians.length)]);
}
}
secondLetter = threadLocal.get();
}
return"" + endians[firstLetter] + secondLetter + System.nanoTime();
}
publicstaticvoidmain(String[] args){
Map<String, String> uniqueKeysTestMap = new ConcurrentHashMap<>();
Thread t1 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t2 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t3 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t4 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t5 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t6 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t7 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
t7.start();
}
}
ACTUALIZACIÓN : Este código funcionará en una sola JVM, pero deberíamos pensar en una JVM distribuida, por lo tanto, estoy pensando en dos soluciones, una con DB y otra sin DB.
con DB
Nombre de la empresa (nombre abreviado 3 caracteres) ---- Número_aleatorio ---- Clave redis específica CONTADOR
(3 caracteres) -------------------------- ---------------------- (2 caracteres) ---------------- (11 caracteres)
No creo que sea posible, pero tienes una buena solución.
corta el final de tu UUID usando subcadena ()
use el código, new Random(System.currentTimeMillis()).nextInt(99999999);
esto generará una identificación aleatoria de hasta 8 caracteres de largo.
generar ID alfanumérico:
char[] chars = "abcdefghijklmnopqrstuvwxyzABSDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray();
Random r = new Random(System.currentTimeMillis());
char[] id = newchar[8];
for (int i = 0; i < 8; i++) {
id[i] = chars[r.nextInt(chars.length)];
}
returnnew String(id);
Respuestas:
No es posible porque un UUID es un número de 16 bytes por definición. Pero, por supuesto, puede generar cadenas únicas de 8 caracteres de largo (consulte las otras respuestas).
También tenga cuidado con generar UUID más largos y subcadenas, ya que algunas partes del ID pueden contener bytes fijos (por ejemplo, este es el caso de los UUID MAC, DCE y MD5).
fuente
Puede probar la
RandomStringUtils
clase de apache.commons :import org.apache.commons.lang3.RandomStringUtils; final int SHORT_ID_LENGTH = 8; // all possible unicode characters String shortId = RandomStringUtils.random(SHORT_ID_LENGTH);
Tenga en cuenta que contendrá todos los caracteres posibles que no son ni URL ni amigables para los humanos.
Así que echa un vistazo a otros métodos también:
// HEX: 0-9, a-f. For example: 6587fddb, c0f182c1 shortId = RandomStringUtils.random(8, "0123456789abcdef"); // a-z, A-Z. For example: eRkgbzeF, MFcWSksx shortId = RandomStringUtils.randomAlphabetic(8); // 0-9. For example: 76091014, 03771122 shortId = RandomStringUtils.randomNumeric(8); // a-z, A-Z, 0-9. For example: WRMcpIk7, s57JwCVA shortId = RandomStringUtils.randomAlphanumeric(8);
Como dijeron otros, la probabilidad de colisión de identificación con identificación más pequeña puede ser significativa. Vea cómo se aplica el problema del cumpleaños a su caso. Puede encontrar una buena explicación sobre cómo calcular la aproximación en esta respuesta .
fuente
org.apache.commons.lang3.RandomStringUtils
está en desuso, sería mejor usarorg.apache.commons.text.RandomStringGenerator
en commons.apache.org/proper/commons-textRandomStringGenerator
, ya que es un código bastante diferente.RandomStringUtils
NO está en desuso. Está destinado a un uso sencillo. ¿Puede proporcionar una fuente de informaciónRandomStringUtils
obsoleta? Puedo proporcionar la documentación de la última versión deRandomStringUtils
como prueba de que no está obsoleta: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…Primero: Incluso los ID únicos generados por java UUID.randomUUID o .net GUID no son 100% únicos. Especialmente UUID.randomUUID es "sólo" un valor aleatorio de 128 bits (seguro). Entonces, si lo reduce a 64 bits, 32 bits, 16 bits (o incluso 1 bit), simplemente se vuelve menos único.
Por lo tanto, es al menos una decisión basada en el riesgo, la duración de su uuid.
Segundo: supongo que cuando habla de "solo 8 caracteres" se refiere a una cadena de 8 caracteres imprimibles normales.
Si desea una cadena única con una longitud de 8 caracteres imprimibles, puede usar una codificación base64. Esto significa 6 bits por carácter, por lo que obtiene 48 bits en total (es posible que no sea muy único, pero tal vez esté bien para su aplicación)
Entonces la forma es simple: cree una matriz aleatoria de 6 bytes
SecureRandom rand; // ... byte[] randomBytes = new byte[16]; rand.nextBytes(randomBytes);
Y luego transfórmalo en una Cadena Base64, por ejemplo,
org.apache.commons.codec.binary.Base64
Por cierto: depende de su aplicación si hay una mejor manera de crear "uuid" que al azar. (Si crea un UUID solo una vez por segundo, entonces es una buena idea agregar una marca de tiempo) (Por cierto: si combina (xor) dos valores aleatorios, el resultado siempre es al menos tan aleatorio como la mayoría aleatorio de los dos).
fuente
Como dijo @Cephalopod, no es posible, pero puede acortar un UUID a 22 caracteres
public static String encodeUUIDBase64(UUID uuid) { ByteBuffer bb = ByteBuffer.wrap(new byte[16]); bb.putLong(uuid.getMostSignificantBits()); bb.putLong(uuid.getLeastSignificantBits()); return StringUtils.trimTrailingCharacter(BaseEncoding.base64Url().encode(bb.array()), '='); }
fuente
Esta es una forma similar que estoy usando aquí para generar un código de error único, basado en la respuesta de Anton Purin, pero confiando en el más apropiado en
org.apache.commons.text.RandomStringGenerator
lugar del (una vez, ya no) en desusoorg.apache.commons.lang3.RandomStringUtils
:@Singleton @Component public class ErrorCodeGenerator implements Supplier<String> { private RandomStringGenerator errorCodeGenerator; public ErrorCodeGenerator() { errorCodeGenerator = new RandomStringGenerator.Builder() .withinRange('0', 'z') .filteredBy(t -> t >= '0' && t <= '9', t -> t >= 'A' && t <= 'Z', t -> t >= 'a' && t <= 'z') .build(); } @Override public String get() { return errorCodeGenerator.generate(8); } }
Todos los consejos sobre colisión siguen siendo válidos, por favor, téngalos en cuenta.
fuente
RandomStringUtils
NO está en desuso. Está destinado a un uso sencillo. ¿Puede proporcionar una fuente de informaciónRandomStringUtils
obsoleta? Puedo proporcionar la documentación de la última versión deRandomStringUtils
como prueba de que no está obsoleta: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…commons.lang
que no esté estrictamente relacionado con el lenguaje en sí, quecommons.text
fue creado con un propósito.RandomStringUtils
no está en desuso y de acuerdo con las referencias proporcionadas por usted, hay una buena razón para mantenerlo en desuso, porque es mucho más simple de usar queRandomStringGenerator
para casos de uso simples. ¿Quizás puedas actualizar tu respuesta? Si / cuandoRandomStringUtils
o su funcionalidad para casos de uso simples se moverá acommons.text
, entonces puede actualizar su respuesta nuevamente, pero actualmente es engañoso.commons.lang
acommons.text
, no hay ninguna razón para que alguien use la primera en lugar de la última, aparte de que ya la esté usando en otro lugar. La simplicidad aquí es bastante subjetiva, encuentro que mi respuesta sigue siendo muy simple y nunca la cambiaría por algo que requiera la importación de Commons Lang.¿Que tal este? En realidad, este código devuelve un máximo de 13 caracteres, pero es más corto que el UUID.
import java.nio.ByteBuffer; import java.util.UUID; /** * Generate short UUID (13 characters) * * @return short UUID */ public static String shortUUID() { UUID uuid = UUID.randomUUID(); long l = ByteBuffer.wrap(uuid.toString().getBytes()).getLong(); return Long.toString(l, Character.MAX_RADIX); }
fuente
getLong()
solo está leyendo los primeros 8 bytes del búfer. El UUID tendrá al menos 36 bytes. ¿Me estoy perdiendo algo porque para mí esto nunca funcionaría?Long.toString(uuid.getLessSignificantBits(), Character.MAX_RADIX)
es mejor.En realidad, quiero un identificador único más corto basado en la marca de tiempo, por lo tanto, probé el siguiente programa.
Es adivinable con
nanosecond + ( endians.length * endians.length )
combinaciones.public class TimStampShorterUUID { private static final Character [] endians = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; private static ThreadLocal<Character> threadLocal = new ThreadLocal<Character>(); private static AtomicLong iterator = new AtomicLong(-1); public static String generateShorterTxnId() { // Keep this as secure random when we want more secure, in distributed systems int firstLetter = ThreadLocalRandom.current().nextInt(0, (endians.length)); //Sometimes your randomness and timestamp will be same value, //when multiple threads are trying at the same nano second //time hence to differentiate it, utilize the threads requesting //for this value, the possible unique thread numbers == endians.length Character secondLetter = threadLocal.get(); if (secondLetter == null) { synchronized (threadLocal) { if (secondLetter == null) { threadLocal.set(endians[(int) (iterator.incrementAndGet() % endians.length)]); } } secondLetter = threadLocal.get(); } return "" + endians[firstLetter] + secondLetter + System.nanoTime(); } public static void main(String[] args) { Map<String, String> uniqueKeysTestMap = new ConcurrentHashMap<>(); Thread t1 = new Thread() { @Override public void run() { while(true) { String time = generateShorterTxnId(); String result = uniqueKeysTestMap.put(time, ""); if(result != null) { System.out.println("failed! - " + time); } } } }; Thread t2 = new Thread() { @Override public void run() { while(true) { String time = generateShorterTxnId(); String result = uniqueKeysTestMap.put(time, ""); if(result != null) { System.out.println("failed! - " + time); } } } }; Thread t3 = new Thread() { @Override public void run() { while(true) { String time = generateShorterTxnId(); String result = uniqueKeysTestMap.put(time, ""); if(result != null) { System.out.println("failed! - " + time); } } } }; Thread t4 = new Thread() { @Override public void run() { while(true) { String time = generateShorterTxnId(); String result = uniqueKeysTestMap.put(time, ""); if(result != null) { System.out.println("failed! - " + time); } } } }; Thread t5 = new Thread() { @Override public void run() { while(true) { String time = generateShorterTxnId(); String result = uniqueKeysTestMap.put(time, ""); if(result != null) { System.out.println("failed! - " + time); } } } }; Thread t6 = new Thread() { @Override public void run() { while(true) { String time = generateShorterTxnId(); String result = uniqueKeysTestMap.put(time, ""); if(result != null) { System.out.println("failed! - " + time); } } } }; Thread t7 = new Thread() { @Override public void run() { while(true) { String time = generateShorterTxnId(); String result = uniqueKeysTestMap.put(time, ""); if(result != null) { System.out.println("failed! - " + time); } } } }; t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); t7.start(); } }
ACTUALIZACIÓN : Este código funcionará en una sola JVM, pero deberíamos pensar en una JVM distribuida, por lo tanto, estoy pensando en dos soluciones, una con DB y otra sin DB.
con DB
Nombre de la empresa (nombre abreviado 3 caracteres) ---- Número_aleatorio ---- Clave redis específica CONTADOR
(3 caracteres) -------------------------- ---------------------- (2 caracteres) ---------------- (11 caracteres)
sin DB
IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ---- epoch milisegundos
(5 caracteres) ----------------- (2char) --------- -------------- (2 caracteres) ----------------- (6 caracteres)
lo actualizará una vez que finalice la codificación.
fuente
No es un UUID, pero esto funciona para mí:
UUID.randomUUID().toString().replace("-","").substring(0,8)
fuente
No creo que sea posible, pero tienes una buena solución.
new Random(System.currentTimeMillis()).nextInt(99999999);
esto generará una identificación aleatoria de hasta 8 caracteres de largo.generar ID alfanumérico:
char[] chars = "abcdefghijklmnopqrstuvwxyzABSDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray(); Random r = new Random(System.currentTimeMillis()); char[] id = new char[8]; for (int i = 0; i < 8; i++) { id[i] = chars[r.nextInt(chars.length)]; } return new String(id);
fuente