Java 在类中强制使用唯一id

Java 在类中强制使用唯一id,java,uniqueidentifier,Java,Uniqueidentifier,仅仅为了思考,如何为给定类的每个实例强制实现属性的唯一性 这里的唯一性可以定义为在单个JVM上和单个用户会话中 这是Java级别的,与数据库无关,主要目的是验证是否发生了冲突 第一个明显的步骤是在类级别拥有一个静态属性 随着实例数量的增加,拥有ArrayList或其他容器似乎不切实际 在类级别递增数字计数器似乎是一种最简单的方法,但id必须始终紧跟在最后使用的id之后 强制使用散列或非数字id可能会有问题 并发性可能值得关注。如果有可能两个实例同时获得一个id,那么应该防止这种情况 如何解决

仅仅为了思考,如何为给定类的每个实例强制实现属性的唯一性

这里的唯一性可以定义为在单个JVM上和单个用户会话中

这是Java级别的,与数据库无关,主要目的是验证是否发生了冲突

第一个明显的步骤是在类级别拥有一个静态属性

  • 随着实例数量的增加,拥有ArrayList或其他容器似乎不切实际
  • 在类级别递增数字计数器似乎是一种最简单的方法,但id必须始终紧跟在最后使用的id之后
  • 强制使用散列或非数字id可能会有问题
  • 并发性可能值得关注。如果有可能两个实例同时获得一个id,那么应该防止这种情况

如何解决这个问题?可能已经存在哪些解决方案/方法?

如果您关心性能,这里有一个线程安全、快速(无锁)和无冲突的唯一id生成版本

    public class Test {
        private static AtomicInteger lastId = new AtomicInteger();
        private int id;

        public Test() {
            id = lastId.incrementAndGet();
        }
...

只需在Java中使用
UUID
类即可。在检查的类中创建UUID类型的字段,并在构造函数中初始化该字段

public class Test  {
   public UUID id;
   public Test() {
      id = UUID.randomUUID();
   }
}
当检测冲突时,只需比较如下对象的UUID的字符串表示形式

Test testObject1 = new Test();
Test testObject2 = new Test();
boolean collision = testObject1.id.toString().equals(testObject2.id.toString());
或者更简单地使用
UUID
类中的
compareTo()
方法

boolean collision=testObject2.id.compareTo(testObject1.id)==0?真:假

0表示
id
s相同+1和-1,当它们不相等时

优点:通用唯一性(可以是基于时间的,随机的),因此应该考虑线程问题(有人应该确认这一点…这是基于我的知识)。更多信息和信息

要使其线程安全,请参阅下面的问题


缺点:需要更改被检查类的结构,即必须在类本身的源中添加
id
字段。这可能很方便,也可能不方便

UUID是一个很好的解决方案,但是后端的UUID.randomUUID()使用方法:

synchronized public void SecureRandom.nextBytes(byte[] bytes) 
所以这很慢:线程在每个id生成操作中锁定一个监视器对象

AtomicInteger更好,因为它在CAS操作中循环。但是,对于每个id生成操作,都必须执行同步操作

在下面的解决方案中,只同步素数生成。同步是在易失性int上进行的,因此是快速和线程安全的。有了一组素数,许多ID在迭代中生成

固定线程数 编辑:固定线程数的解决方案

我知道有多少线程将使用Id生成,然后可以使用值生成Id

Id = I mod X + n*X
其中X是线程数,I是线程数,N是一个局部变量,该变量在每次生成Id时递增。此解决方案的代码非常简单,但必须与hole程序基础结构集成

由素数生成的ID 其思想是将ID生成为素数的因子 id=p_1^f1*p_2^f2*p_2^f3*。*p_n^fn

我们在每个线程中使用不同的素数来在每个线程中生成不同的ID集

假设我们使用素数(2,3,5),序列为:

2, 2^2, 2^3, 2^4, 2^5,..., 2^64
然后,当我们看到将生成溢出时,我们将因子滚动到下一个素数:

3, 2*3 , 2^2*3, 2^3*3, 2^4*3, 2^5*3,..., 2^62*3
接下来呢

3^2, 2*3^2 , 2^2*3^2, .....
世代班 编辑:必须在AtomicInteger上生成正确的初级订单

IdFactorialGenerator类的每个实例将生成不同的ID集

要生成线程保存的ID,只需使用ThreadLocal设置每个线程的实例。同步仅在素数生成期间实现

package eu.pmsoft.sam.idgenerator;

public class IdFactorialGenerator {
    private static AtomicInteger nextPrimeNumber = 0;

    private int usedSlots;
    private int[] primes = new int[64];
    private int[] factors = new int[64];
    private long id;
    public IdFactorialGenerator(){
        usedSlots = 1;
        primes[0] = Sieve$.MODULE$.primeNumber(nextPrimeNumber.getAndAdd(1));
        factors[0] = 1;
        id = 1;
    }

    public long nextId(){
        for (int factorToUpdate = 0; factorToUpdate < 64; factorToUpdate++) {
            if(factorToUpdate == usedSlots) {
                factors[factorToUpdate] = 1;
                primes[factorToUpdate] = Sieve$.MODULE$.primeNumber(nextPrimeNumber.getAndAdd(1));
                usedSlots++;
            }
            int primeToExtend = primes[factorToUpdate];
            if( primeToExtend < Long.MAX_VALUE / id) {
                // id * primeToExtend < Long.MAX_VALUE
                factors[factorToUpdate] = factors[factorToUpdate]*primeToExtend;
                id = id*primeToExtend;
                return id;
            } else {
                factors[factorToUpdate] = 1;
                id = 1;
                for (int i = 0; i < usedSlots; i++) {
                    id = id*factors[i];
                }
            }
        }
        throw new IllegalStateException("I can not generate more ids");
    }
}
包eu.pmsoft.sam.idgenerator;
公共类IdFactorialGenerator{
私有静态AtomicInteger nextPrimeNumber=0;
私人内部使用DSlots;
私有整数[]素数=新整数[64];
私有整数[]因子=新整数[64];
私人长id;
公共IdFactorialGenerator(){
usedSlots=1;
素数[0]=Sieve$.MODULE$.primeNumber(nextPrimeNumber.getAndAdd(1));
系数[0]=1;
id=1;
}
公共长nextId(){
对于(int-factorUpdate=0;factorUpdate<64;factorUpdate++){
if(factorUpdate==usedSlots){
因子[FactorUpdate]=1;
primes[factorUpdate]=Sieve$.MODULE$.primeNumber(nextPrimeNumber.getAndAdd(1));
使用dslots++;
}
int primeToExtend=素数[factorUpdate];
if(primeToExtend
为了得到素数,我使用了问题7中提供的scala实现:

对象筛选{
def素数(位置:Int):Int=ps(位置)
private lazy val ps:Stream[Int]=2#:::Stream.from(3.filter)(i=>
ps.takeWhile(j=>j*j0))
}

如果不指定“唯一性”的范围,这个问题是无法回答的,这就是为什么我怀疑有人投票关闭了。在空间和时间上定义你所说的唯一性。也就是说,在单个JVM调用中是唯一的,跨越所有空间和时间,或者介于两者之间。感谢您请求clari
object Sieve {

  def primeNumber(position: Int): Int = ps(position)

  private lazy val ps: Stream[Int] = 2 #:: Stream.from(3).filter(i =>
    ps.takeWhile(j => j * j <= i).forall(i % _ > 0))
}