一意番号採番

一意番号を採番したい。なお、採番処理はミリ秒より短い間隔で呼ばれる。DBは使わないため、sequenceは使えない。できれば、一意であるだけでなく、採番された番号の大小が逆転しないほうがよい。採番結果はユーザに見せるかもしれない。

いくつかブレストしてみた。

結論

方法1-(1) と方法2のMIX。
まず、スレッドID+ナノ秒でお手軽に採番(方法1-(1))し、重複した場合は、採番しなおす。(方法2)


ソースの雰囲気


public class Hoge{
private String saiban(){
return Thread.currentThread().getId() + "" + System.nanoTime() + "";
}
public void regist(){
MyObject obj = new MyObject(saiban());
boolean ok = false;
while(!ok){
try{
controller.add();
// addして重複したら DuplicateExceptionが発生する。
}catch(DuplicateException e){
// 重複エラーが発生したら、少し(1ミリ秒)待って採番しなおす。
Tread.sleep(1);
obj.setCode(saiban());
continue;
}
ok = true;
}
}
}

方法1:まじめにユニーク番号を採番する

(1) スレッドID+ナノ秒

コード:Thread.currentThread().getId() + "" + System.nanoTime() + ""
結果例:125507895897104
⇒お手軽。ナノ秒より短いタームで採番することがなければOK。一応同一VM内想定でマルチスレッド対応済。
⇒別VMで採番することがあれば、再検討。(PIDつけるとか)
※採番した順に並ぶ


補足:
coLinuxで動作させたところ、マイクロ秒単位でしか採番できなかった(下3桁常に000)。gettimeofdayが内部的に使われている??
一応、JDKのバージョンをメモっておく。


java version "1.5.0_15"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_15-b04)
Java HotSpot(TM) Client VM (build 1.5.0_15-b04, mixed mode, sharing)

(2) 年月日時分秒ミリ秒+乱数

コード:SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + Math.round(Math.random() * 1000000);
結果例:20080317120054482293158
⇒重複する確率は低いが、ある。この案はシャレです。ブレストのひとつとして使えるくらい。ナノ秒+乱数のほうがよいかも。
※採番した順に並ぶ

(3) java.util.UUID(16進)

コード:java.util.UUID.randomUUID().toString()
結果例:7a6b162e-69ca-4102-96f8-f64c0bf2bb73
※一意性が保証されているわけではないが、重複する可能性は天文学的に低い。(SDN Forumより)
⇒採番結果をユーザに見せるなら、「何これ」と言われそう。
※randomUUIDなので、採番した順に並ばない。

(4) java.util.UUID(10進)

コード:Math.abs(java.util.UUID.randomUUID().hashCode()) + ""
結果例:1958590224
⇒可変なので、固定長にしたければフォーマットかける。
※randomUUIDなので、採番した順に並ばない。

(5) 時分秒ミリ秒+UUIDの10進表現の絶対値

コード:SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + Math.abs(java.util.UUID.randomUUID().hashCode())
結果例:200803171526078371544808851
⇒UUIDの絶対値を使うことにより重複する可能性があがるため、イマイチ。
※randomUUIDなので、採番した順に並ばない。

(6)スタティックな採番簿(変数)を使う

コード:private static long cnt=0;public static String saiban(){cnt +=1;return cnt + "";}public static reset(){cnt=0;}
結果例:1
もうちょっとまじめに、Singletonで実装してもよい。リセットするタイミングを検討する必要がある。定石だが、美しくない。
sequenceイメージなら、この案。

方法2:重複したらやり直せばよい。

(1) 重複がなくなるまで採番しなおす

例えば年月日時分秒ミリ秒+乱数を使い、採番した番号をチェックし、重複したら重複しなくなるまで採番しなおす。
⇒アリかも。

(2) 重複したらちょっと待って採番しなおす

例えばナノ秒を使い、重複したら1ナノ秒待って採番しなおす。
⇒アリかも。