I am attempting to generate unqiue numeric ids to be used as primary keys in a Mysql database. I need to generate them outside of the database because of the distributed nature of the system.
Here is my attempt:
@RunWith(MockitoJUnitRunner.class)
public class AbstractEntityTest {
@Test
public void testGenerateUniqueId() {
val ids = new HashSet<Long>();
val duplicates = new HashSet<Long>();
for (int i = 0; i < 1000000; i++) {
val id = System.currentTimeMillis() +
(ThreadLocalRandom.current().nextLong(9999999) + ThreadLocalRandom.current().nextLong(9999999));
if (!ids.add(id)) {
duplicates.add(id);
}
}
System.out.println("ids: " + ids.size());
System.out.println("Duplicates: " + duplicates.size());
assertThat(duplicates).isEmpty();
}
}
The result is:
ids: 967265
Duplicates: 31939
Can anyone suggest a better way of generating a truely unique long
in Java?
A solution appears to be:
@Test
public void testGenerateUniqueId_withUUID() {
val ids = new HashSet<Long>();
val duplicates = new HashSet<Long>();
for (int i = 0; i < 1000000; i++) {
val id = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE;
if (!ids.add(id)) {
duplicates.add(id);
}
}
System.out.println("ids: " + ids.size());
System.out.println("Duplicates: " + duplicates.size());
assertThat(duplicates).isEmpty();
}
The result is:
ids: 1000000
Duplicates: 0
Here is 5 iterations of 30 million generations:
@Test
public void testGenerateUniqueId_withUUID() {
val iterations = 5;
for (int j = 0; j < iterations; j++) {
val ids = new HashSet<Long>();
val duplicates = new HashSet<Long>();
for (int i = 0; i < 30000000; i++) {
val id = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE;
if (!ids.add(id)) {
duplicates.add(id);
}
}
System.out.println(String.format("Iteration %s of %s", j + 1, iterations));
System.out.println("ids: " + ids.size());
System.out.println("Duplicates: " + duplicates.size());
assertThat(duplicates).isEmpty();
}
}
The result is:
Iteration 1 of 5
ids: 30000000
Duplicates: 0
Iteration 2 of 5
ids: 30000000
Duplicates: 0
Iteration 3 of 5
ids: 30000000
Duplicates: 0
Iteration 4 of 5
ids: 30000000
Duplicates: 0
Iteration 5 of 5
ids: 30000000
Duplicates: 0
I have added in the current time in milliseconds to assist with uniqueness outside of the running process.
@Test
public void testGenerateUniqueId_withUUID_andCurrentTimeMilliseconds() {
val iterations = 5;
val duplicates = new HashSet<Long>();
for (int j = 0; j < iterations; j++) {
val ids = new HashSet<Long>();
for (int i = 0; i < 30000000; i++) {
val id = System.currentTimeMillis() + UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE;
if (!ids.add(id)) {
duplicates.add(id);
}
}
System.out.println(String.format("Iteration %s of %s", j + 1, iterations));
System.out.println("ids: " + ids.size());
System.out.println("Duplicates: " + duplicates.size());
assertThat(duplicates).isEmpty();
}
}