Chỉ tạo UUID 8 ký tự


82

Các thư viện UUID tạo ra các UUID 32 ký tự.

Tôi muốn tạo UUID chỉ 8 ký tự, có được không?


Chắc chắn rồi. Nhưng nó có thể không đơn giản và ngắn hơn ít có khả năng thực sự là duy nhất. Vậy tại sao?

@delnan, được sử dụng trong môi trường nhúng?
Allen Zhang

1
Nếu chuỗi kết quả có thể được lưu trữ trong UTF-8, bạn có thể có 4 byte cho mỗi ký tự. Nếu bạn có thể sử dụng toàn bộ phạm vi đó, bạn sẽ chỉ cần 4 ký tự UTF-8 để đại diện cho cùng một thông tin.
EECOLOR

Câu trả lời:


72

Không thể thực hiện được vì UUID là một số 16 byte cho mỗi định nghĩa. Nhưng tất nhiên, bạn có thể tạo chuỗi duy nhất dài 8 ký tự (xem các câu trả lời khác).

Ngoài ra, hãy cẩn thận với việc tạo các UUID dài hơn và nhập chuỗi con của chúng, vì một số phần của ID có thể chứa các byte cố định (ví dụ: đây là trường hợp với các UUID MAC, DCE và MD5).


làm thế nào về timestamp
anna poorani

60

Bạn có thể thử RandomStringUtils lớp từ 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);

Xin lưu ý rằng nó sẽ chứa tất cả các ký tự có thể có, không phải là URL hoặc không thân thiện với con người.

Vì vậy, hãy kiểm tra các phương pháp khác:

// 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); 

Như những người khác đã nói xác suất xung đột id với id nhỏ hơn có thể đáng kể. Kiểm tra cách vấn đề sinh nhật áp dụng cho trường hợp của bạn. Bạn có thể tìm thấy lời giải thích hay về cách tính xấp xỉ trong câu trả lời này .


4
org.apache.commons.lang3.RandomStringUtilskhông được dùng nữa nên tốt hơn bạn nên sử dụng org.apache.commons.text.RandomStringGeneratortrong commons.apache.org/proper/commons-text
BrunoJCM

Đã thêm một câu trả lời mới cho RandomStringGenerator, vì nó là mã hoàn toàn khác.
BrunoJCM

2
Chỉ là FYI cho người xem tương lai, Tính ngẫu nhiên không đảm bảo tính duy nhất. Máy phát ngẫu nhiên đảm bảo tính ngẫu nhiên; và có thể tạo ra một tập hợp các số ngẫu nhiên hợp lệ với các giá trị lặp lại.
Vishnu Prasad V

RandomStringUtilsKHÔNG bị phản đối. Nó được thiết kế để sử dụng đơn giản. Bạn có thể cung cấp nguồn thông tin không RandomStringUtilsđược dùng nữa không? Tôi có thể cung cấp tài liệu về phiên bản mới nhất của nó RandomStringUtilsđể làm bằng chứng rằng nó không còn được dùng nữa: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…
krm

Chỉ bằng cách kiểm tra bản đồ hoặc bộ băm với uuids đã được sử dụng, xác suất xảy ra va chạm là rất lớn.
Anton

18

Thứ nhất: Ngay cả các ID duy nhất được tạo bởi java UUID.randomUUID hoặc .net GUID cũng không phải là duy nhất 100%. Đặc biệt UUID.randomUUID là "chỉ" một giá trị ngẫu nhiên 128 bit (an toàn). Vì vậy, nếu bạn giảm nó xuống 64 bit, 32 bit, 16 bit (hoặc thậm chí 1 bit) thì nó sẽ trở nên ít độc đáo hơn.

Vì vậy, nó ít nhất là một quyết định dựa trên rủi ro, uuid của bạn phải là bao lâu.

Thứ hai: Tôi giả sử rằng khi bạn nói về "chỉ 8 ký tự", bạn có nghĩa là một Chuỗi 8 ký tự có thể in bình thường.

Nếu bạn muốn một chuỗi duy nhất có độ dài 8 ký tự có thể in được, bạn có thể sử dụng mã hóa base64. Điều này có nghĩa là 6 bit cho mỗi ký tự, vì vậy bạn nhận được tổng cộng 48 bit (có thể không phải là duy nhất - nhưng có thể nó là ok cho ứng dụng của bạn)

Vì vậy, cách rất đơn giản: tạo một mảng ngẫu nhiên 6 byte

 SecureRandom rand;
 // ...
 byte[] randomBytes = new byte[16];
 rand.nextBytes(randomBytes);

Và sau đó chuyển đổi nó thành Chuỗi Base64, ví dụ bằng cách org.apache.commons.codec.binary.Base64

BTW: nó phụ thuộc vào ứng dụng của bạn nếu có cách tốt hơn để tạo "uuid" sau đó ngẫu nhiên. (Nếu bạn chỉ tạo UUID một lần mỗi giây, thì bạn nên thêm dấu thời gian) (Nhân tiện: nếu bạn kết hợp (xor) hai giá trị ngẫu nhiên, kết quả luôn ít nhất là ngẫu nhiên nhất ngẫu nhiên của cả hai).


7

Như @Cephalopod đã nêu là không thể nhưng bạn có thể rút ngắn UUID còn 22 ký tự

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

2

Đây là một cách tương tự mà tôi đang sử dụng ở đây để tạo mã lỗi duy nhất, dựa trên câu trả lời Anton Purin, nhưng dựa trên câu trả lời thích hợp hơn org.apache.commons.text.RandomStringGeneratorthay vì (một lần, không nữa) không được chấp nhận 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);
    }

}

Tất cả các lời khuyên về va chạm vẫn được áp dụng, vui lòng lưu ý chúng.


RandomStringUtilsKHÔNG bị phản đối. Nó được thiết kế để sử dụng đơn giản. Bạn có thể cung cấp nguồn thông tin không RandomStringUtilsđược dùng nữa không? Tôi có thể cung cấp tài liệu về phiên bản mới nhất của nó RandomStringUtilsđể làm bằng chứng rằng nó không còn được dùng nữa: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…
krm

Chà, nếu bạn tìm hiểu sâu hơn một chút, bạn sẽ thấy rằng, kể từ thời điểm viết câu trả lời này, bản phát hành mới nhất đã thực sự không dùng lớp này: github.com/apache/commons-lang/commits/master/src / main / java / org /… Có lẽ một số phản hồi ( user.commons.apache.narkive.com/GVBG2Ar0/… ) đã làm cho nó hoạt động trở lại. Dù sao thì bạn cũng không nên sử dụng bất cứ thứ gì commons.langkhông liên quan chặt chẽ đến ngôn ngữ, commons.textđược tạo ra với một mục đích.
BrunoJCM

Cảm ơn bạn đã giải thích BrunoJCM. Tại thời điểm hiện tại, RandomStringUtilsnó không bị phản đối và theo các tài liệu tham khảo do bạn cung cấp, có lý do chính đáng để giữ nó không bị phản đối, vì nó dễ sử dụng hơn nhiều so RandomStringGeneratorvới các trường hợp sử dụng đơn giản. Có lẽ bạn có thể cập nhật câu trả lời của bạn? Nếu / khi nào RandomStringUtilshoặc chức năng của nó cho các trường hợp sử dụng đơn giản sẽ được chuyển đến commons.text, thì bạn có thể cập nhật lại câu trả lời của mình, nhưng hiện tại câu trả lời đang gây hiểu lầm.
krm

Đã thêm một lưu ý, nhưng một lần nữa, rõ ràng là dự án Apache Commons đang di chuyển văn bản utils từ commons.langsang commons.text, không có lý do gì để bất kỳ ai sử dụng cái trước chứ không phải cái sau ngoài việc sử dụng nó ở một nơi khác. Đơn giản ở đây là khá chủ quan, tôi thấy câu trả lời của tôi vẫn còn rất đơn giản và tôi sẽ không bao giờ thay đổi nó cho một cái gì đó yêu cầu phải nhập Commons Lang.
BrunoJCM

1

Còn cái này thì sao? Trên thực tế, mã này trả về tối đa 13 ký tự, nhưng nó ngắn hơn 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);
}

4
Bạn biết rằng getLong()chỉ đọc 8 byte đầu tiên của bộ đệm. UUID sẽ có ít nhất 36 byte. Tôi có thiếu một cái gì đó không vì với tôi điều này sẽ không bao giờ hiệu quả.
Edwin Dalorzo

2
8 byte đầu tiên là các bit quan trọng nhất của UUID. theo câu trả lời này, các bit ít quan trọng hơn là ngẫu nhiên. Như vậy Long.toString(uuid.getLessSignificantBits(), Character.MAX_RADIX)là tốt hơn.
DouO

1

Trên thực tế, tôi muốn mã nhận dạng duy nhất ngắn hơn dựa trên dấu thời gian, do đó đã thử chương trình bên dưới.

Nó có thể đoán được với các nanosecond + ( endians.length * endians.length )kết hợp.

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

CẬP NHẬT : Mã này sẽ hoạt động trên JVM đơn lẻ, nhưng chúng ta nên suy nghĩ về JVM phân tán, do đó tôi đang nghĩ đến hai giải pháp một với DB và một giải pháp khác không có DB.

với DB

Tên công ty (tên viết tắt 3 ký tự) ---- Random_Number ---- Key cụ thể redis COUNTER
(3 ký tự ) -------------------------- ---------------------- (2 ký tự) ---------------- (11 ký tự)

không có DB

IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ---- epoch mili giây
(5 ký tự) ----------------- (2char) --------- -------------- (2 ký tự) ----------------- (6 ký tự)

sẽ cập nhật cho bạn sau khi mã hóa hoàn tất.



-11

Tôi không nghĩ rằng điều đó là có thể nhưng bạn có một cách giải quyết tốt.

  1. cắt phần cuối của UUID của bạn bằng cách sử dụng chuỗi con ()
  2. sử dụng mã new Random(System.currentTimeMillis()).nextInt(99999999); này sẽ tạo ID ngẫu nhiên dài tối đa 8 ký tự.
  3. tạo id chữ và số:

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

14
Thật không may, tất cả các cách tiếp cận này có khả năng cung cấp cho bạn số lần lặp lại (tức là Id không phải duy nhất) sớm hơn bạn muốn.
Stephen C

1
Không phải gieo hạt với ngày hiện tại ít ngẫu nhiên hơn so với việc sử dụng hàm tạo trống?
Patrick Favre
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.