Pewnie. Ale prawdopodobnie nie jest tak proste i krótsze, co oznacza mniejsze prawdopodobieństwo, że będzie faktycznie wyjątkowe. Więc dlaczego?
@delnan, do użytku w środowisku osadzonym?
Allen Zhang
1
Jeśli wynikowy ciąg może być przechowywany w UTF-8, potencjalnie masz 4 bajty na znak. Jeśli możesz użyć całego zakresu, potrzebujesz tylko 4 znaków UTF-8 do reprezentowania tej samej informacji.
EECOLOR
Odpowiedzi:
72
Nie jest to możliwe, ponieważ UUID to 16-bajtowa liczba według definicji. Oczywiście możesz wygenerować 8-znakowe unikalne ciągi (zobacz inne odpowiedzi).
Zachowaj również ostrożność przy generowaniu dłuższych UUID i tworzeniu ich podciągów, ponieważ niektóre części identyfikatora mogą zawierać ustalone bajty (np. Tak jest w przypadku UUID MAC, DCE i MD5).
Jak powiedzieli inni, prawdopodobieństwo kolizji identyfikatora z mniejszym identyfikatorem może być znaczące. Sprawdź, jak problem z datą urodzin dotyczy Twojego przypadku. W tej odpowiedzi można znaleźć ładne wyjaśnienie, jak obliczyć przybliżenie .
Ponieważ org.apache.commons.lang3.RandomStringUtilsjest przestarzałe, lepiej byłoby używać org.apache.commons.text.RandomStringGeneratorw commons.apache.org/proper/commons-text
BrunoJCM
Dodano nową odpowiedź RandomStringGenerator, ponieważ jest to zupełnie inny kod.
BrunoJCM
2
To tylko informacja dla przyszłych widzów, Randomness nie gwarantuje wyjątkowości. Losowe generatory gwarantują losowość; i może wygenerować prawidłowy zestaw liczb losowych z powtarzającymi się wartościami.
Vishnu Prasad V
RandomStringUtilsNIE jest przestarzałe. Jest przeznaczony do prostego użytkowania. Czy możesz podać źródło RandomStringUtilsprzestarzałych informacji? Mogę dostarczyć dokumentację najnowszej wersji RandomStringUtilsjako dowód, że nie jest przestarzała: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…
krm
Tylko sprawdzając mapę lub hashset z już używanymi uuidami, prawdopodobieństwo kolizji jest ogromne.
Anton
18
Po pierwsze: nawet unikalne identyfikatory generowane przez java UUID.randomUUID lub .net GUID nie są w 100% unikalne. Szczególnie UUID.randomUUID jest „tylko” 128-bitową (bezpieczną) wartością losową. Więc jeśli zredukujesz go do 64 bitów, 32 bitów, 16 bitów (lub nawet 1 bitu), stanie się po prostu mniej wyjątkowy.
Jest to więc decyzja oparta przynajmniej na ryzyku, jak długo musi trwać twój płyn.
Po drugie: zakładam, że kiedy mówisz o „tylko 8 znakach”, masz na myśli ciąg 8 normalnych drukowalnych znaków.
Jeśli chcesz mieć unikalny ciąg o długości 8 drukowalnych znaków, możesz użyć kodowania base64. Oznacza to 6 bitów na znak, więc w sumie otrzymujesz 48 bitów (możliwe, że niezbyt unikalne - ale może jest to w porządku dla twojej aplikacji)
Sposób jest prosty: utwórz 6-bajtową losową tablicę
A następnie przekształć go w łańcuch Base64, na przykład przez org.apache.commons.codec.binary.Base64
Przy okazji: od aplikacji zależy, czy istnieje lepszy sposób tworzenia „uuid” niż losowo. (Jeśli tworzysz identyfikatory UUID tylko raz na sekundę, dobrym pomysłem jest dodanie znacznika czasu) (Przy okazji: jeśli połączysz (xor) dwie losowe wartości, wynik jest zawsze co najmniej tak przypadkowy losowy z obu).
Jest to podobny sposób, którego używam tutaj, aby wygenerować unikalny kod błędu, oparty na odpowiedzi Antona Purina, ale polegający na bardziej odpowiednim org.apache.commons.text.RandomStringGeneratorzamiast (kiedyś, już nie) przestarzałym 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);
}
}
Wszystkie porady dotyczące kolizji nadal obowiązują, należy o nich pamiętać.
RandomStringUtilsNIE jest przestarzałe. Jest przeznaczony do prostego użytkowania. Czy możesz podać źródło informacji, które RandomStringUtilssą przestarzałe? Mogę dostarczyć dokumentację najnowszej wersji programu RandomStringUtilsjako dowód, że nie jest przestarzała: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…
Dziękuję za wyjaśnienie BrunoJCM. W chwili obecnej RandomStringUtilsnie jest przestarzały i zgodnie z dostarczonymi przez Ciebie referencjami jest dobry powód, aby go nie używać, ponieważ jest znacznie prostszy w użyciu niż w RandomStringGeneratorprzypadku prostych przypadków użycia. Może możesz zaktualizować swoją odpowiedź? Jeśli / kiedy RandomStringUtilslub jego funkcjonalność dla prostych przypadków użycia zostanie przeniesiona do commons.text, możesz ponownie zaktualizować swoją odpowiedź, ale obecnie jest ona myląca.
krm
Dodano uwagę, ale znowu jest jasne, że projekt Apache Commons przenosi narzędzia tekstowe z commons.langdo commons.text, nie ma powodu, aby ktokolwiek używał tego pierwszego, a nie drugiego, poza tym, że używa go już gdzie indziej. Prostota jest tutaj raczej subiektywna, uważam, że moja odpowiedź jest nadal bardzo prosta i nigdy nie zmieniłbym jej na coś, co wymagałoby importu Commons Lang.
BrunoJCM,
1
A co z tym? W rzeczywistości ten kod zwraca maksymalnie 13 znaków, ale jest krótszy niż 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);
}
Wiesz, że getLong()czyta tylko pierwsze 8 bajtów bufora. UUID będzie miał co najmniej 36 bajtów. Czy coś mi brakuje, bo to mi się nigdy nie uda.
Edwin Dalorzo
2
Pierwsze 8 bajtów to najbardziej znaczące bity identyfikatora UUID. zgodnie z tą odpowiedzią mniej znaczące bity są bardziej losowe. Więc Long.toString(uuid.getLessSignificantBits(), Character.MAX_RADIX)jest lepiej.
DouO
0
Właściwie chcę krótszego unikalnego identyfikatora opartego na znaczniku czasu, dlatego wypróbowałem poniższy program.
Można to zgadnąć z nanosecond + ( endians.length * endians.length ) kombinacji.
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();
}
}
UPDATE : Ten kod będzie działał na pojedynczej JVM, ale powinniśmy pomyśleć o rozproszonej JVM, stąd myślę o dwóch rozwiązaniach, jednym z DB, a drugim bez DB.
z DB
Nazwa firmy (nazwa skrócona 3 znaki) ---- Random_Number ---- Specyficzne dla klucza redis COUNTER
(3 ) -------------------------- ---------------------- (2 znaki) ---------------- (11 znaków)
Nie sądzę, żeby to było możliwe, ale masz dobre obejście.
odetnij koniec swojego UUID za pomocą funkcji substring ()
użyj kodu new Random(System.currentTimeMillis()).nextInt(99999999);
spowoduje to wygenerowanie losowego identyfikatora o długości do 8 znaków.
wygeneruj identyfikator alfanumeryczny:
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);
Odpowiedzi:
Nie jest to możliwe, ponieważ UUID to 16-bajtowa liczba według definicji. Oczywiście możesz wygenerować 8-znakowe unikalne ciągi (zobacz inne odpowiedzi).
Zachowaj również ostrożność przy generowaniu dłuższych UUID i tworzeniu ich podciągów, ponieważ niektóre części identyfikatora mogą zawierać ustalone bajty (np. Tak jest w przypadku UUID MAC, DCE i MD5).
źródło
Możesz wypróbować
RandomStringUtils
zajęcia z 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);
Pamiętaj, że będzie zawierał wszystkie możliwe znaki, które nie są ani URL, ani przyjazne dla człowieka.
Sprawdź też inne metody:
// 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);
Jak powiedzieli inni, prawdopodobieństwo kolizji identyfikatora z mniejszym identyfikatorem może być znaczące. Sprawdź, jak problem z datą urodzin dotyczy Twojego przypadku. W tej odpowiedzi można znaleźć ładne wyjaśnienie, jak obliczyć przybliżenie .
źródło
org.apache.commons.lang3.RandomStringUtils
jest przestarzałe, lepiej byłoby używaćorg.apache.commons.text.RandomStringGenerator
w commons.apache.org/proper/commons-textRandomStringGenerator
, ponieważ jest to zupełnie inny kod.RandomStringUtils
NIE jest przestarzałe. Jest przeznaczony do prostego użytkowania. Czy możesz podać źródłoRandomStringUtils
przestarzałych informacji? Mogę dostarczyć dokumentację najnowszej wersjiRandomStringUtils
jako dowód, że nie jest przestarzała: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…Po pierwsze: nawet unikalne identyfikatory generowane przez java UUID.randomUUID lub .net GUID nie są w 100% unikalne. Szczególnie UUID.randomUUID jest „tylko” 128-bitową (bezpieczną) wartością losową. Więc jeśli zredukujesz go do 64 bitów, 32 bitów, 16 bitów (lub nawet 1 bitu), stanie się po prostu mniej wyjątkowy.
Jest to więc decyzja oparta przynajmniej na ryzyku, jak długo musi trwać twój płyn.
Po drugie: zakładam, że kiedy mówisz o „tylko 8 znakach”, masz na myśli ciąg 8 normalnych drukowalnych znaków.
Jeśli chcesz mieć unikalny ciąg o długości 8 drukowalnych znaków, możesz użyć kodowania base64. Oznacza to 6 bitów na znak, więc w sumie otrzymujesz 48 bitów (możliwe, że niezbyt unikalne - ale może jest to w porządku dla twojej aplikacji)
Sposób jest prosty: utwórz 6-bajtową losową tablicę
SecureRandom rand; // ... byte[] randomBytes = new byte[16]; rand.nextBytes(randomBytes);
A następnie przekształć go w łańcuch Base64, na przykład przez
org.apache.commons.codec.binary.Base64
Przy okazji: od aplikacji zależy, czy istnieje lepszy sposób tworzenia „uuid” niż losowo. (Jeśli tworzysz identyfikatory UUID tylko raz na sekundę, dobrym pomysłem jest dodanie znacznika czasu) (Przy okazji: jeśli połączysz (xor) dwie losowe wartości, wynik jest zawsze co najmniej tak przypadkowy losowy z obu).
źródło
Jak stwierdził @Cephalopod, nie jest to możliwe, ale możesz skrócić UUID do 22 znaków
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()), '='); }
źródło
Jest to podobny sposób, którego używam tutaj, aby wygenerować unikalny kod błędu, oparty na odpowiedzi Antona Purina, ale polegający na bardziej odpowiednim
org.apache.commons.text.RandomStringGenerator
zamiast (kiedyś, już nie) przestarzałymorg.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); } }
Wszystkie porady dotyczące kolizji nadal obowiązują, należy o nich pamiętać.
źródło
RandomStringUtils
NIE jest przestarzałe. Jest przeznaczony do prostego użytkowania. Czy możesz podać źródło informacji, któreRandomStringUtils
są przestarzałe? Mogę dostarczyć dokumentację najnowszej wersji programuRandomStringUtils
jako dowód, że nie jest przestarzała: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…commons.lang
co i tak nie jest ściśle związane z samym językiem,commons.text
zostało stworzone w określonym celu.RandomStringUtils
nie jest przestarzały i zgodnie z dostarczonymi przez Ciebie referencjami jest dobry powód, aby go nie używać, ponieważ jest znacznie prostszy w użyciu niż wRandomStringGenerator
przypadku prostych przypadków użycia. Może możesz zaktualizować swoją odpowiedź? Jeśli / kiedyRandomStringUtils
lub jego funkcjonalność dla prostych przypadków użycia zostanie przeniesiona docommons.text
, możesz ponownie zaktualizować swoją odpowiedź, ale obecnie jest ona myląca.commons.lang
docommons.text
, nie ma powodu, aby ktokolwiek używał tego pierwszego, a nie drugiego, poza tym, że używa go już gdzie indziej. Prostota jest tutaj raczej subiektywna, uważam, że moja odpowiedź jest nadal bardzo prosta i nigdy nie zmieniłbym jej na coś, co wymagałoby importu Commons Lang.A co z tym? W rzeczywistości ten kod zwraca maksymalnie 13 znaków, ale jest krótszy niż 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); }
źródło
getLong()
czyta tylko pierwsze 8 bajtów bufora. UUID będzie miał co najmniej 36 bajtów. Czy coś mi brakuje, bo to mi się nigdy nie uda.Long.toString(uuid.getLessSignificantBits(), Character.MAX_RADIX)
jest lepiej.Właściwie chcę krótszego unikalnego identyfikatora opartego na znaczniku czasu, dlatego wypróbowałem poniższy program.
Można to zgadnąć z
nanosecond + ( endians.length * endians.length )
kombinacji.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(); } }
UPDATE : Ten kod będzie działał na pojedynczej JVM, ale powinniśmy pomyśleć o rozproszonej JVM, stąd myślę o dwóch rozwiązaniach, jednym z DB, a drugim bez DB.
z DB
Nazwa firmy (nazwa skrócona 3 znaki) ---- Random_Number ---- Specyficzne dla klucza redis COUNTER
(3 ) -------------------------- ---------------------- (2 znaki) ---------------- (11 znaków)
bez DB
IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ---- epoka milisekundy
(5 znaków) ----------------- (2 znaki) --------- -------------- (2 znaki) ----------------- (6 znaków)
poinformuje Cię po zakończeniu kodowania.
źródło
Nie UUID, ale to działa dla mnie:
UUID.randomUUID().toString().replace("-","").substring(0,8)
źródło
Nie sądzę, żeby to było możliwe, ale masz dobre obejście.
new Random(System.currentTimeMillis()).nextInt(99999999);
spowoduje to wygenerowanie losowego identyfikatora o długości do 8 znaków.wygeneruj identyfikator alfanumeryczny:
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);
źródło