Biblioteki UUID generują 32-znakowe identyfikatory UUID.
Chcę wygenerować tylko 8-znakowe identyfikatory UUID, czy to możliwe?
Biblioteki UUID generują 32-znakowe identyfikatory UUID.
Chcę wygenerować tylko 8-znakowe identyfikatory UUID, czy to możliwe?
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).
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 .
org.apache.commons.lang3.RandomStringUtils
jest przestarzałe, lepiej byłoby używać org.apache.commons.text.RandomStringGenerator
w commons.apache.org/proper/commons-text
RandomStringGenerator
, ponieważ jest to zupełnie inny kod.
RandomStringUtils
NIE jest przestarzałe. Jest przeznaczony do prostego użytkowania. Czy możesz podać źródło RandomStringUtils
przestarzałych informacji? Mogę dostarczyć dokumentację najnowszej wersji RandomStringUtils
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).
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()), '=');
}
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łym org.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ć.
RandomStringUtils
NIE jest przestarzałe. Jest przeznaczony do prostego użytkowania. Czy możesz podać źródło informacji, które RandomStringUtils
są przestarzałe? Mogę dostarczyć dokumentację najnowszej wersji programu RandomStringUtils
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ż w RandomStringGenerator
przypadku prostych przypadków użycia. Może możesz zaktualizować swoją odpowiedź? Jeśli / kiedy RandomStringUtils
lub 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.
commons.lang
do 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.
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);
}
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.
Nie UUID, ale to działa dla mnie:
UUID.randomUUID().toString().replace("-","").substring(0,8)
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);