首页 > 编程语言 >Java实现生成永不重复的数字方案详解

Java实现生成永不重复的数字方案详解

时间:2025-01-10 14:32:54浏览次数:3  
标签:Java 数字 永不 long 生成 public 详解 ID UUID

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

在上一期的文章中,我们深入探讨了如何通过 Java 反射机制 来创建静态和非静态内部类,解决了动态类实例化中的一些常见难题。通过反射的灵活操作,我们能够在运行时动态生成对象并调用它们的相关方法,极大地提升了程序的扩展性与灵活性。然而,在现代应用开发中,另一个常见的需求是生成 永不重复的数字。无论是在订单系统中生成唯一订单号,还是分布式系统中生成唯一标识,生成不重复的数字或ID都是至关重要的。

本期我们将关注 Java 生产永不重复的数字,通过多个角度剖析不同场景下的解决方案,结合具体的代码和实际应用场景,帮助开发者选择适合的实现方式。

摘要

本文以 Java 实现生成永不重复的数字 为核心,详细介绍了几种不同的实现方法,包括简单的自增算法、基于时间戳的生成方式、UUID 的使用,以及在分布式系统中常见的雪花算法。每种方法都有其适用的场景和优势。通过源码解析、实际使用案例分享和测试用例,我们将探讨如何在不同场景下生成唯一且不重复的数字或标识符,并分析各方法的优缺点,帮助开发者选择适合自己业务的最佳方案。

概述

在现代应用中,生成唯一且不重复的数字是一项关键任务,尤其是在分布式系统和多线程环境中。例如:

  • 电商系统中生成唯一订单号
  • 社交网络中为用户生成唯一的ID
  • 分布式数据库中生成唯一的主键

常见的生成方式

  1. 自增数字:最简单的生成唯一数字的方式,即通过一个全局递增的数字生成器。
  2. 时间戳结合随机数:通过系统当前时间(时间戳)加上随机数来生成不重复的数字。
  3. UUID:Java 自带的 UUID 类,能够生成几乎保证全局唯一的标识符。
  4. 雪花算法(Snowflake):Twitter 提出的分布式系统中生成全局唯一ID的算法。

每种方式都有不同的使用场景,我们将逐一分析。

源码解析

1. 自增数字生成器

最简单的方式是使用自增数字,通过维护一个全局变量,每次生成一个数字时,将其自增。对于单线程环境或简单的需求场景,这种方式非常有效。

public class IncrementalNumberGenerator {
    private static long currentNumber = 0;

    // 线程安全的自增方法
    public static synchronized long getNextNumber() {
        return ++currentNumber;
    }
}
代码解析:
  • currentNumber 作为静态变量,存储当前的数字。
  • getNextNumber 方法使用 synchronized 关键字确保线程安全,在并发环境下防止多线程同时修改 currentNumber 的问题。

2. 时间戳结合随机数生成

时间戳(毫秒级)结合随机数生成唯一数字的方式较为常见,能够在较大范围内保证唯一性。

import java.util.Random;

public class TimestampRandomNumberGenerator {
    private static final Random random = new Random();

    public static String generateUniqueNumber() {
        long timestamp = System.currentTimeMillis();
        int randomNumber = random.nextInt(1000); // 随机生成0-999的数字
        return timestamp + String.format("%03d", randomNumber); // 拼接时间戳和随机数
    }
}
代码解析:
  • System.currentTimeMillis() 获取当前时间戳(单位:毫秒)。
  • 使用 Random 类生成一个三位随机数。
  • 将时间戳和随机数拼接成一个字符串,保证唯一性。

3. UUID 生成

UUID(Universally Unique Identifier) 是一种128位的全局唯一标识符,Java 中的 java.util.UUID 类能够直接生成不重复的 UUID。

import java.util.UUID;

public class UUIDGenerator {
    public static String generateUUID() {
        return UUID.randomUUID().toString();
    }
}
代码解析:
  • UUID.randomUUID() 生成一个随机的 UUID。
  • UUID 通常由32个字符组成,包含字母和数字,格式如 550e8400-e29b-41d4-a716-446655440000

4. 雪花算法(Snowflake)

雪花算法是一种分布式环境下生成唯一ID的算法,由 Twitter 提出,它能够在分布式系统中生成64位的全局唯一ID。其ID由时间戳、机器ID和序列号组成,能保证在高并发情况下生成不重复的数字。

public class SnowflakeIdGenerator {
    private final long twepoch = 1288834974657L;
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final long sequenceBits = 12L;
    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("Worker ID can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("Datacenter ID can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    public synchronized long nextId() {
        long timestamp = timeGen();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate ID");
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        return ((timestamp - twepoch) << timestampLeftShift) |
               (datacenterId << datacenterIdShift) |
               (workerId << workerIdShift) |
               sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }
}
代码解析:
  • 时间戳:用于确保生成的ID按照时间顺序递增。
  • 机器ID和数据中心ID:用于在分布式系统中标识不同的机器和数据中心,防止ID冲突。
  • 序列号:在同一毫秒内生成多个ID时,用于区分这些ID。

雪花算法生成的ID是一个64位长的整数,能够在分布式环境下保证唯一性,且生成速度非常快。

使用案例分享

案例 1:基于自增数字生成订单号

对于中小型电商平台,生成唯一订单号的方式可以通过自增数字结合业务标识来完成。如下所示:

public class OrderService {
    private static long orderId = 0;

    public synchronized static String generateOrderNumber() {
        return "ORDER" + (++orderId);
    }
}

案例 2:分布式系统中的唯一标识生成

对于分布式系统,雪花算法是一种常见的解决方案。下面是一个分布式用户ID生成的示例:

public class UserIdGenerator {
    private static final SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1); // 假设机器ID和数据中心ID为1

    public static long generateUserId() {
        return idGenerator.nextId();
    }
}

应用场景案例

  1. 订单号生成:在电商系统中,需要为每个订单生成唯一的订单号,避免重复的订单处理和数据混乱。

  2. 分布式系统中的唯一标识生成:在分布式架构中,多个节点同时进行任务时,生成全局唯一的ID是保障数据

一致性的关键。

优缺点分析

自增数字

  • 优点:实现简单,易于管理。
  • 缺点:仅适用于单机环境,多线程环境下需要同步处理,且不适合分布式系统。

时间戳结合随机数

  • 优点:能够在大多数场景下保证唯一性,生成速度较快。
  • 缺点:在高并发环境下有可能出现重复,随机数的范围较小。

UUID

  • 优点:能够生成几乎全局唯一的标识,且使用简单。
  • 缺点:UUID较长,不适合需要短ID的场景。

雪花算法

  • 优点:适合分布式环境,能够保证生成ID的唯一性和有序性。
  • 缺点:实现较为复杂,需要合理配置机器ID和数据中心ID。

核心类方法介绍

System.currentTimeMillis()

返回当前时间的毫秒数,自1970年1月1日开始计算。

Random.nextInt(int bound)

生成一个在 [0, bound) 范围内的随机整数。

UUID.randomUUID()

生成一个128位的随机UUID。

SnowflakeIdGenerator.nextId()

生成一个唯一的64位ID,用于分布式环境下的唯一标识生成。

测试用例

用例1:测试自增数字生成

@Test
public void testIncrementalNumberGeneration() {
    long num1 = IncrementalNumberGenerator.getNextNumber();
    long num2 = IncrementalNumberGenerator.getNextNumber();
    assertNotEquals(num1, num2);
}

代码解析:

如下是具体的代码解析,希望对大家有所帮助:

这段Java代码定义了一个测试方法 testIncrementalNumberGeneration,用于测试增量数字生成器是否能够生成不同的连续数字。

下面是这段代码的详细解读:

  1. @Test:这是一个JUnit注解,表示接下来的方法是测试方法。

  2. public void testIncrementalNumberGeneration() { ... }:定义了一个名为 testIncrementalNumberGeneration 的测试方法。

  3. long num1 = IncrementalNumberGenerator.getNextNumber();:调用 IncrementalNumberGenerator 类的静态方法 getNextNumber 来生成第一个数字,并将其存储在变量 num1 中。

  4. long num2 = IncrementalNumberGenerator.getNextNumber();:再次调用 getNextNumber 方法生成第二个数字,并将其存储在变量 num2 中。

  5. assertNotEquals(num1, num2);:使用 assertNotEquals 断言方法来验证 num1num2 是否不同。如果两个数字不相同,测试将通过;如果相同,则测试将失败。

总结:这个测试用例的目的是验证增量数字生成器生成的两个连续数字是否不相同。增量数字生成器通常用于确保每个生成的数字都是唯一的,并且每个后续数字都比前一个大,这在生成序列号、版本号等时非常有用。

注意:代码中假设 IncrementalNumberGenerator 类已经定义,并且它的 getNextNumber 方法能够生成连续的数字。此外,测试方法的名称表明它专注于数字生成器的功能,确保每次调用 getNextNumber 方法都能得到一个更大的数字。如果 IncrementalNumberGenerator 是多线程安全的,那么即使在并发环境下,这个测试也应该能够通过。

用例2:测试雪花算法生成唯一ID

@Test
public void testSnowflakeIdGeneration() {
    SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);
    long id1 = generator.nextId();
    long id2 = generator.nextId();
    assertNotEquals(id1, id2);
}

代码解析:

如下是具体的代码解析,希望对大家有所帮助:

这段Java代码定义了一个测试方法 testSnowflakeIdGeneration,用于测试雪花算法(Snowflake Algorithm)ID生成器是否能够生成不同的ID。

下面是这段代码的详细解读:

  1. @Test:这是一个JUnit注解,表示接下来的方法是测试方法。

  2. public void testSnowflakeIdGeneration() { ... }:定义了一个名为 testSnowflakeIdGeneration 的测试方法。

  3. SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);:创建了 SnowflakeIdGenerator 类的一个实例,这个类可能是一个实现了Twitter雪花算法的ID生成器。它的构造函数接受两个参数,通常表示数据中心ID和机器ID。

  4. long id1 = generator.nextId();:调用 generator 实例的 nextId 方法生成第一个ID,并将其存储在变量 id1 中。

  5. long id2 = generator.nextId();:再次调用 nextId 方法生成第二个ID,并将其存储在变量 id2 中。

  6. assertNotEquals(id1, id2);:使用 assertNotEquals 断言方法来验证 id1id2 是否不同。如果两个ID不相同,测试将通过;如果相同,则测试将失败。

总结:这个测试用例的目的是验证ID生成器生成的两个连续ID是否不相同。雪花算法ID生成器通常用于分布式系统中生成唯一的ID,它结合了时间戳、数据中心ID和机器ID来确保生成的ID的唯一性。

小结

本文通过多种方案介绍了如何在 Java 中生成永不重复的数字。从简单的自增数字到适用于分布式环境的雪花算法,各种方案适用于不同的场景。对于单机环境,简单的自增数字或时间戳结合随机数足够使用,而在分布式环境下,雪花算法则成为了最佳选择。

总结

Java 生成不重复数字的方案多种多样,开发者需要根据具体的应用场景选择最合适的方案。本文从单机环境到分布式系统,依次分析了自增、时间戳结合随机数、UUID和雪花算法,并提供了相关代码和案例。掌握这些方案,可以帮助开发者在实际项目中应对不同的唯一标识生成需求,保证系统的稳定性和数据的一致性。

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。

标签:Java,数字,永不,long,生成,public,详解,ID,UUID
From: https://blog.csdn.net/weixin_66592566/article/details/142287736

相关文章

  • python+django/flask的惠安租房管理平台java+nodejs+php-计算机毕业设计
    目录技术栈和环境说明具体实现截图预期达到的目标系统设计详细视频演示技术路线解决的思路性能/安全/负载方面可行性分析论证python-flask核心代码部分展示python-django核心代码部分展示研究方法感恩大学老师和同学源码获取技术栈和环境说明本系统以Python开发语言......
  • python+django/flask的会议室预定系统java+nodejs+php-计算机毕业设计
    目录技术栈和环境说明具体实现截图预期达到的目标系统设计详细视频演示技术路线解决的思路性能/安全/负载方面可行性分析论证python-flask核心代码部分展示python-django核心代码部分展示研究方法感恩大学老师和同学源码获取技术栈和环境说明本系统以Python开发语言......
  • 责任链模式详解
    责任链模式详解1.定义责任链模式(ChainofResponsibilityPattern)是一种行为型设计模式,它允许将请求沿着处理者链进行传递,直到某个处理者能够处理该请求为止。这种模式通过将请求的发送者和接收者解耦,将请求沿着一条链传递,直到链上的某个节点能够处理该请求。2.主要角色......
  • java后端 进行 url 编码和解码
    有备考软考的小伙伴,想积分落户,单位评职称?科目太多不知道怎么选?考试考点难点太多没有头绪?刚准备1个多月过了高级,关注我,我整理了软考各科目的报考条件、适合人群以及备考攻略,可以直接领取:https://d.51cto.com/bLN8S1Java后端进行URL编码和解码在现代的Web开发中,URL编码和解码是一......
  • Java 实现 Elasticsearch 查询当前索引全部数据
    Java实现Elasticsearch查询当前索引全部数据需求背景通常情况Java实现查询Elasticsearch全部数据写在最后需求背景通常情况下,Elasticsearch为了提高查询效率,对于不指定分页查询条数的查询语句,默认会返回10条数据。那么这就会有一种情况,当你需要一次性返回Ela......
  • Java基于SpringBoot的企业客源关系管理系统
    文章目录详细视频演示项目介绍技术介绍功能介绍核心代码系统效果图详细视频演示文章底部名片,获取项目的完整演示视频,免费解答技术疑问项目介绍  随着市场竞争的加剧和消费者需求的多样化,企业越来越意识到建立和维护良好的客户关系对于实现可持续发展的重要性。......
  • 详解SonarQube Web API的使用方法以及典型应用场景(内附python代码)
    SonarQubeWebAPISonarQube的WebAPI是一组HTTPRESTAPI,允许开发人员与SonarQube服务器进行交互。这些API涵盖了SonarQube的各个方面,包括项目管理、问题管理、质量规则和指标等。我们可以在SonarQube的帮助菜单中查看相关使用信息,如下图所示:典型应用场景SonarQubeAPI可......
  • 协同过滤算法私人诊所系统|Java|SpringBoot|VUE| 
                 【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SpringBoot、Mybatis-Plus、VUE、jquery,html5⃣️数据库可视化工具:navicat6⃣️服务器:SpringBoot自带apach......
  • For循环详解
    有错请指出for本是一个单词,意思有:给,对;为了;关于;代表;受雇于;意思是;支持;因为;为得到;换取;就……而言;……后(更好、更快乐等);(表示去向)往;(安排或预定)在……时;对(某人)来说(困难、必需、愉快等);以……为价格;(表示一段时间)计;表示一系列事件之一在C++中,它的格式是这样的:for(init;conditio......
  • Java学习记录
    面向对象封装对象代表什么,就得封装对应数据,并提供数据对应行为例子1:人画圆对象:圆、人则画圆的方法应该写在圆的类中(画圆会对应到圆的半径等数据)publicclassCircle{doubleradius;publicvoiddraw(){System.out.println("根据半径"+radius+"......