web-dev-qa-db-ja.com

8文字のみのUUIDの生成

UUIDライブラリは、32文字のUUIDを生成します。

8文字のみのUUIDを生成したいのですが、可能ですか?

62
M.J.

IDは定義ごとに16バイトの数値であるため不可能です。しかし、もちろん、8文字の一意の文字列を生成できます(他の回答を参照)。

また、IDの一部に固定バイトが含まれる可能性があるため(たとえば、MAC、DCE、およびMD5 UUIDの場合)、より長いUUIDの生成とサブストリング化にも注意してください。

57
Cephalopod

RandomStringUtilsApache.commonsのクラス を試すことができます:

import org.Apache.commons.lang3.RandomStringUtils;

final int SHORT_ID_LENGTH = 8;

// all possible unicode characters
String shortId = RandomStringUtils.random(SHORT_ID_LENGTH);

URLや人間に優しいものではない可能性のあるすべての文字が含まれることに注意してください。

他の方法もチェックしてください:

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

他の人が言ったように、より小さいidとのid衝突の可能性は重要です。 誕生日の問題 がどのようにあなたのケースに当てはまるかを確認してください。 this answer で近似値を計算する方法の説明を見つけることができます。

31
Anton Purin

最初:Java UUID.randomUUIDまたは.net GUIDによって生成される一意のIDでさえ、100%一意ではありません。特にUUID.randomUUIDは、128ビット(安全な)ランダム値「のみ」です。したがって、64ビット、32ビット、16ビット(または1ビット)に減らすと、一意性が低下します。

だから、少なくともリスクベースの決定、あなたのUUIDがどれだけ長くなければならないかです。

2番目:「8文字のみ」について話すとき、通常の印刷可能な8文字の文字列を意味すると思います。

長さが8文字の印刷可能な文字を含む一意の文字列が必要な場合は、base64エンコードを使用できます。これは1文字あたり6ビットを意味するため、合計で48ビットを取得します(非常にユニークではない可能性があります-しかし、おそらくあなたのアプリケーションには問題ありません)

方法は簡単です:6バイトのランダム配列を作成します

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

そして、それを例えばorg.Apache.commons.codec.binary.Base64によってBase64文字列に変換します

ところで:「uuid」をランダムに作成するより良い方法があるかどうかは、アプリケーションに依存します。 (1秒間に1回だけUUIDを作成する場合は、タイムスタンプを追加することをお勧めします)両方のランダム)。

15
Ralph

@Cephalopodが述べたように、それは不可能ですが、UUIDを22文字に短縮できます

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

これは、Anton Purinの回答に基づいて一意のエラーコードを生成するためにここで使用しているのと同様の方法ですが、非推奨org.Apache.commons.text.RandomStringGeneratorではなく、より適切な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);
    }

}

衝突に関するすべてのアドバイスは引き続き適用されますので、注意してください。

3
BrunoJCM

実際には、タイムスタンプベースの短い一意の識別子が必要なので、以下のプログラムを試しました。

nanosecond + ( endians.length * endians.length )の組み合わせで推測できます。

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:このコードは単一のJVMで動作しますが、分散JVMで考える必要があります。したがって、DBを使用するソリューションとDBを使用しないソリューションの2つを考えています。

dBを使用

会社名(ショートネーム3文字)----ランダム番号----キー固有のredis COUNTER
(3文字)----------------------------------------- -------(2文字)----------------(11文字)

dBなし

IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ----エポックミリ秒
(5文字)-----------------(2char)--------------------- -(2文字)-----------------(6文字)

コーディングが完了すると更新されます。

1