How to create user friendly unique IDs, UUIDs or other unique identifiers in Java

32,589

Solution 1

I assume that UUIDs get generated equally through the whole range of the 128 Bit range of the UUID.

First off, your assumption may be incorrect, depending on the UUID type (1, 2, 3, or 4). From the Java UUID docs:

There exist different variants of these global identifiers. The methods of this class are for manipulating the Leach-Salz variant, although the constructors allow the creation of any variant of UUID (described below).

The layout of a variant 2 (Leach-Salz) UUID is as follows: The most significant long consists of the following unsigned fields:

0xFFFFFFFF00000000 time_low 
0x00000000FFFF0000 time_mid 
0x000000000000F000 version 
0x0000000000000FFF time_hi  

The least significant long consists of the following unsigned fields:

0xC000000000000000 variant 
0x3FFF000000000000 clock_seq 
0x0000FFFFFFFFFFFF node  

The variant field contains a value which identifies the layout of the UUID. The bit layout described above is valid only for a UUID with a variant value of 2, which indicates the Leach-Salz variant.

The version field holds a value that describes the type of this UUID. There are four different basic types of UUIDs: time-based, DCE security, name-based, and randomly generated UUIDs. These types have a version value of 1, 2, 3 and 4, respectively.

The best way to do what you're doing is to generate a random string with code that looks something like this (source):

public class RandomString {

          public static String randomstring(int lo, int hi){
                  int n = rand(lo, hi);
                  byte b[] = new byte[n];
                  for (int i = 0; i < n; i++)
                          b[i] = (byte)rand('a', 'z');
                  return new String(b, 0);
          }

          private static int rand(int lo, int hi){
                      java.util.Random rn = new java.util.Random();
                  int n = hi - lo + 1;
                  int i = rn.nextInt(n);
                  if (i < 0)
                          i = -i;
                  return lo + i;
          }

          public static String randomstring(){
                  return randomstring(5, 25);
          }

        /**
         * @param args
         */
        public static void main(String[] args) {
                System.out.println(randomstring());

        }

}

If you're incredibly worried about collisions or something, I suggest you base64 encode your UUID which should cut down on its size.

Moral of the story: don't rely on individual parts of UUIDs as they are holistically designed. If you do need to rely on individual parts of a UUID, make sure you familiarize yourself with the particular UUID type and implementation.

Solution 2

Here is another approach for generating user friendly IDs:
http://thedailywtf.com/Articles/The-Automated-Curse-Generator.aspx

(But you should go for the bad-word-filter)

Solution 3

Any UUID/Guid is just 16 Bytes of data. These 16 bytes can be easily encoded using BASE64 (or BASE64url), then stripped off all of the "=" characters at the end of the string.

This gives a nice, short string which still holds the same data as the UUID/Guid. In other words, it is possible to recreate the UUID/Guid from that data if such becomes necessary.

Solution 4

Here's a way to generate a URL-friendly 22-character UUID

public static String generateShortUuid() {
        UUID uuid = UUID.randomUUID();

        long lsb = uuid.getLeastSignificantBits();
        long msb = uuid.getMostSignificantBits();

        byte[] uuidBytes = ByteBuffer.allocate(16).putLong(msb).putLong(lsb).array();

        // Strip down the '==' at the end and make it url friendly   
        return Base64.encode(uuidBytes)
                    .substring(0, 22)
                    .replace("/", "_")
                    .replace("+", "-");
    }

For your use-case, it would be better to track a running count of registered user, and for each value, generate a string-token like this:

public static String longToReverseBase62(long value /* must be positive! */) {

        final char[] LETTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();

        StringBuilder result = new StringBuilder(9);
        do {
            result.append(LETTERS[(int)(value % 62)]);
            value /= 62l;
        }
        while (value != 0);

        return result.toString();
    }

For security reasons, it would be better if you make the values non-sequential, so each time a user registers, you can increment the value let's say by 1024 (This would be good to generate uuids for 2^64 / 2^10 = 2^54 users which is quite certainly more than you'd ever need :)

Solution 5

At the time of this writing, this question's title is:

How to create user friendly unique IDs, UUIDs or other unique identifiers in Java

The question of generating a user-friendly ID is a subjective one. If you have a unique value, there are many ways to format it into a "user-friendly" one, and they all come down to mapping unique values one-to-one with "user-friendly" IDs — if the input value was unique, the "user-friendly" ID will likewise be unique.

In addition, it's not possible in general to create a random value that's also unique, at least if each random value is generated independently of any other. In addition, there are many things you should ask yourself if you want to generate unique identifiers (which come from my section on unique random identifiers):

  1. Can the application easily check identifiers for uniqueness within the desired scope and range (e.g., check whether a file or database record with that identifier already exists)?
  2. Can the application tolerate the risk of generating the same identifier for different resources?
  3. Do identifiers have to be hard to guess, be simply "random-looking", or be neither?
  4. Do identifiers have to be typed in or otherwise relayed by end users?
  5. Is the resource an identifier identifies available to anyone who knows that identifier (even without being logged in or authorized in some way)?
  6. Do identifiers have to be memorable?

In your case, you have several conflicting goals: You want identifiers that are unique, random, and easy to type by end users. But other things you should think about are:

  • Are other users allowed to access the resource identified by the ID, whenever they know the ID? If not, then additional access control or a longer key length will be necessary.
  • Can your application tolerate the risk of duplicate keys? If so, then the keys can be completely randomly generated (such as by a cryptographic RNG such as java.security.SecureRandom in Java). If not, then your goal will be harder to achieve, especially for keys intended for security purposes.

Also, if you want IDs that have to be typed in by end users, you should consider choosing a character set carefully or allowing typing mistakes to be detected.

Share:
32,589
basZero
Author by

basZero

WORK: Web App Developer, Java Enthusiast since 1995 HOME: I'm in love with photography

Updated on July 30, 2020

Comments

  • basZero
    basZero almost 4 years

    I usually use the UUID class to generate unique IDs. This works fine if these IDs are used by technical systems only, they don't care how long they are:

    System.out.println(UUID.randomUUID().toString());
    
    > 67849f28-c0af-46c7-8421-94f0642e5d4d
    

    Is there a nice way to create user friendly unique IDs (like those from tinyurl) which are a bit shorter than the UUIDs? Usecase: you want to send out IDs via Mail to your customers which in turn visit your site and enter that number into a form, like a voucher ID.

    I assume that UUIDs get generated equally through the whole range of the 128 Bit range of the UUID. So would it be sage to use just the lower 64 Bits for instance?

    System.out.println(UUID.randomUUID().getLeastSignificantBits());
    

    Any feedback is welcome.

  • Esko Luontola
    Esko Luontola over 13 years
    If that method is called twice within one millisecond, it will return the same value due to seeding the pseudo-random number generator with the same seed.
  • Rich
    Rich over 13 years
    Side note: For cryptographic applications you probably don't want to use Random. There is also SecureRandom which should be a lot harder to predict (may or may not be an issue, but things like session keys are an application where you don't want a naïve LCG). What the OP descibes with voucher IDs could also be a case where you don't want a random person claim someone else's.
  • Rich
    Rich over 13 years
    Side note: For cryptographic applications you probably don't want to use Random. There is also SecureRandom which should be a lot harder to predict (may or may not be an issue, but things like session keys are an application where you don't want a naïve LCG). What the OP descibes with voucher IDs could also be a case where you don't want a random person claim someone else's.
  • David Titarenco
    David Titarenco over 13 years
    Joey has a good point. It depends whether or not your app needs to be cryptographically secure or not.
  • Rich
    Rich over 13 years
    Oh, and you probably should move the random instance to the class body instead of having it in the method. Seeding is done with the current time and you only want to see once, not every time the method is called.
  • Lior
    Lior almost 10 years
    This is incorrect. You are only using 64-bit (Size of one long) instead of the full 128-bit of the UUID.
  • Ky -
    Ky - over 8 years
    using System.nanoTime() would also be more reliable.
  • Loïc Faugeron
    Loïc Faugeron about 8 years
    The base 64 encoded version of 67849f28-c0af-46c7-8421-94f0642e5d4d is Njc4NDlmMjgtYzBhZi00NmM3LTg0MjEtOTRmMDY0MmU1ZDRk. So this fallback method doesn't actually cut back the size.
  • aux
    aux over 7 years
    Take the 128 bit number form of UUID (not the string) and then base-64 version is shorter indeed: 22 chars.