java UUID

2021/02/20 Java初级进阶 共 11372 字,约 33 分钟
闷骚的程序员

1. java生成UUID。有什么通用的类吗?

在Java中生成UUID(Universally Unique Identifier)可以使用Java标准库中的java.util.UUID类。这个类提供了静态方法来生成不同版本(Version 1、Version 3、Version 4、Version 5)的UUID。通常情况下,我们使用Version 4的UUID,因为它是基于随机数生成的,足够满足大多数场景的唯一性需求。

以下是生成Version 4 UUID的示例代码:

import java.util.UUID;

public class Main {
    public static void main(String[] args) {
        // 生成Version 4的UUID
        UUID uuid = UUID.randomUUID();
        System.out.println("Generated UUID: " + uuid.toString());
    }
}

在这段代码中,UUID.randomUUID()方法会返回一个新的随机生成的Version 4 UUID,并将其打印出来。

需要注意的是,UUID是128位的数字,通常以32个字符的形式表示(包括4个短划线),例如:”550e8400-e29b-41d4-a716-446655440000”。每次调用UUID.randomUUID()都会生成一个新的UUID,因此它具有很高的唯一性。

2. 如何去除-呢?

要去除UUID中的短划线(-),可以使用UUID类的toString方法得到UUID的字符串表示,然后使用replaceAll方法去除短划线。下面是一个示例代码:

import java.util.UUID;

public class Main {
    public static void main(String[] args) {
        // 生成Version 4的UUID
        UUID uuid = UUID.randomUUID();
        
        // 去除短划线
        String uuidWithoutHyphens = uuid.toString().replaceAll("-", "");
        
        System.out.println("UUID with hyphens: " + uuid.toString());
        System.out.println("UUID without hyphens: " + uuidWithoutHyphens);
    }
}

在这个示例中,replaceAll("-","")会将UUID字符串中的短划线全部替换为空字符串,从而得到没有短划线的UUID字符串。

需要注意的是,UUID本身是一个128位的数字,去除短划线后得到的字符串长度会变为32个字符。

3. 详细列举java中生成UUID的所有方式

在Java中生成UUID主要通过java.util.UUID类实现。UUID类支持生成和操作多种版本的UUID。下面详细列举了Java中生成UUID的所有常见方式及其不同版本的特性:

1. 使用 UUID.randomUUID() 生成 Version 4 UUID

这是最常用的生成UUID的方式,基于随机数生成UUID。Version 4 UUID使用随机数生成128位的标识符,保证了极高的唯一性。

import java.util.UUID;

public class Main {
    public static void main(String[] args) {
        UUID uuid = UUID.randomUUID(); // Version 4 UUID
        System.out.println("Version 4 UUID: " + uuid);
    }
}

特点:

  • 基于随机数
  • 唯一性非常高,适用于大多数场景
  • 生成速度快

2. 使用 UUID.nameUUIDFromBytes() 生成 Version 3 或 Version 5 UUID

UUID.nameUUIDFromBytes(byte[] name)可以根据命名空间生成UUID。UUID的版本由哈希函数决定:

  • Version 3 UUID 是基于MD5哈希生成的。
  • Version 5 UUID 是基于SHA-1哈希生成的。
import java.util.UUID;

public class Main {
    public static void main(String[] args) {
        // 使用字节数组生成基于命名空间的UUID (Version 3)
        byte[] name = "example.com".getBytes();
        UUID uuidV3 = UUID.nameUUIDFromBytes(name); // Version 3 (MD5)
        System.out.println("Version 3 UUID: " + uuidV3);
    }
}

特点:

  • 基于命名空间和输入的数据生成
  • 生成相同输入时返回相同的UUID(确定性)
  • Version 3使用MD5哈希;Version 5使用SHA-1哈希

3. 通过 UUID.fromString() 方法创建UUID

UUID.fromString(String uuid)可以从一个合法的UUID字符串创建UUID对象。此方法不生成新的UUID,而是解析现有的UUID字符串。

import java.util.UUID;

public class Main {
    public static void main(String[] args) {
        String uuidStr = "550e8400-e29b-41d4-a716-446655440000";
        UUID uuid = UUID.fromString(uuidStr);
        System.out.println("UUID from string: " + uuid);
    }
}

特点:

  • 适用于将已有的UUID字符串转换为UUID对象

4. 自定义基于时间的UUID (Version 1) - Java 不提供直接API

Java标准库中没有直接提供生成Version 1(基于时间的UUID)的API,但可以通过第三方库(如Apache Commons Id、UUID Generator等)来生成Version 1 UUID。

如果一定要使用Version 1 UUID,可以使用类似如下的第三方库:

<!-- Maven依赖 -->
<dependency>
    <groupId>com.fasterxml.uuid</groupId>
    <artifactId>java-uuid-generator</artifactId>
    <version>4.0.1</version>
</dependency>

然后通过如下代码生成Version 1 UUID:

import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.TimeBasedGenerator;

public class Main {
    public static void main(String[] args) {
        TimeBasedGenerator uuidGenerator = Generators.timeBasedGenerator();
        UUID uuidV1 = uuidGenerator.generate();
        System.out.println("Version 1 UUID: " + uuidV1);
    }
}

特点:

  • 基于时间戳生成UUID(使用当前时间)
  • 包含物理网络地址(MAC地址),适合分布式系统唯一性要求较高的场景

5. 使用第三方库生成不同版本UUID

除了Java标准库,您还可以使用第三方库来生成不同版本的UUID,比如Apache Commons IdJUG (Java UUID Generator)等。

使用 Apache Commons Id

<!-- Maven依赖 -->
<dependency>
    <groupId>commons-id</groupId>
    <artifactId>commons-id</artifactId>
    <version>0.1</version>
</dependency>

示例代码:

import org.apache.commons.id.uuid.UUID;

public class Main {
    public static void main(String[] args) {
        UUID uuidV1 = UUID.timeUUID(); // Version 1
        System.out.println("Apache Commons Version 1 UUID: " + uuidV1);
    }
}

使用 Java UUID Generator (JUG)

<!-- Maven依赖 -->
<dependency>
    <groupId>com.fasterxml.uuid</groupId>
    <artifactId>java-uuid-generator</artifactId>
    <version>4.0.1</version>
</dependency>

6. 自定义 UUID

你还可以自定义生成UUID的规则。例如,在基于某些算法或应用程序逻辑的场景中创建UUID(这种情况下,你不依赖于UUID类,而是创建自己的生成规则)。


总结

Java中的UUID生成方式主要有以下几种:

  1. Version 4 UUID - 使用 UUID.randomUUID() 基于随机数生成。
  2. Version 3/5 UUID - 使用 UUID.nameUUIDFromBytes(byte[] name) 基于命名空间和MD5或SHA-1哈希生成。
  3. 从字符串生成UUID - 使用 UUID.fromString(String uuid)
  4. Version 1 UUID - 通过第三方库(如JUGApache Commons Id)生成基于时间戳的UUID。

通常,Java原生的 UUID.randomUUID()(Version 4)生成方式最常用,因为它生成的UUID足够唯一且不需要额外的依赖。

4. uuid一定唯一吗?

UUID(Universally Unique Identifier)是一种全局唯一标识符,通常用于标识信息或对象。UUID 的标准定义确保了在一定条件下生成的 UUID 是唯一的,但并不是绝对的唯一。

UUID 的唯一性基于以下几点:

  1. 标准定义: 标准的 UUID 版本(如 UUIDv1、UUIDv4)会结合时间戳、MAC 地址、随机数等因素生成 UUID,从而保证在一定概率下生成的 UUID 是唯一的。

  2. 算法保证: UUID 的生成算法会考虑当前时间、节点信息、随机数等因素,确保生成的 UUID 在同一时空范围内不会重复。

尽管 UUID 在理论上是唯一的,但在实际应用中仍有一些需要注意的情况:

  • 碰撞概率: 虽然 UUID 的碰撞概率非常低,但仍然存在极小的可能性。特别是在短时间内生成大量 UUID 时,碰撞的风险可能会增加。

  • 随机性: 随机生成的 UUID 在同一时刻可能会出现相同的值,尤其是在使用 UUIDv4 时,因为其部分内容是基于随机数生成的。

因此,在使用 UUID 时,应该根据具体情况评估其唯一性的要求,以及可能存在的碰撞风险。对于绝对唯一性要求极高的场景,可能需要额外的措施来确保唯一性,例如使用数据库的唯一索引或者其他业务逻辑来检查。

5. 如何避免uuid重复呢?

要避免 UUID 重复,可以考虑以下几种方法:

  1. 使用不同版本的UUID: UUID 有不同的版本(如 UUIDv1、UUIDv4),每个版本生成 UUID 的方式不同。可以根据具体需求选择合适的版本,以降低重复的可能性。

  2. 使用数据库唯一索引: 在数据库中存储 UUID 时,可以为 UUID 列添加唯一索引,确保数据库中的 UUID 值不重复。

  3. 限制生成频率: 在生成 UUID 的过程中,可以限制生成的频率,避免在短时间内生成大量 UUID,从而减少碰撞的可能性。

  4. 结合其他信息生成UUID: 可以在生成 UUID 的过程中,结合其他信息(如时间戳、节点信息等)生成 UUID,增加唯一性的保证。

  5. 持久化已使用的UUID: 可以将已经使用过的 UUID 记录下来,避免再次使用相同的 UUID。

  6. 定期检查和清理: 定期检查系统中已经使用的 UUID,及时清理不再需要的 UUID,避免重复使用。

组合使用以上方法可以有效地降低 UUID 重复的可能性。根据具体情况,可以选择其中一种或多种方法来保证 UUID 的唯一性。

6. HUTOOL返回uuid自动去除_?

在 Hutool 中,生成 UUID 默认是带有横线的,如果你想生成一个没有横线的 UUID,你可以使用 IdUtil.fastUUID() 方法,然后通过 .replace("-", "") 去掉横线,或者直接使用 IdUtil.fastSimpleUUID(),它会自动返回一个不带横线的 UUID。

具体代码如下:

// 使用 fastUUID 生成带横线的 UUID,然后手动去除横线
String uuidWithHyphen = IdUtil.fastUUID();
String uuidWithoutHyphen = uuidWithHyphen.replace("-", "");
System.out.println(uuidWithoutHyphen);

// 使用 fastSimpleUUID 直接生成不带横线的 UUID
String simpleUUID = IdUtil.fastSimpleUUID();
System.out.println(simpleUUID);

这样你就可以生成不带横线的 UUID 了。

7. UUID和雪花算法在应用,原理和使用场景上有什么区别呢?

UUID(通用唯一识别码)和雪花算法(Snowflake)是两种常见的分布式系统中生成唯一标识的方案。它们在原理、使用场景、以及优缺点上有很大区别。下面是详细的比较:

1. UUID

原理:

UUID(Universally Unique Identifier)是一种标准化的 128 位标识符,通常分为 5 个部分,用 36 个字符(包含 4 个横线)表示,例如 123e4567-e89b-12d3-a456-426614174000

UUID 通常有几种生成方式,常见的有以下几类:

  • UUID Version 1:基于时间戳和节点的 MAC 地址生成,带有一定的时间序列性。
  • UUID Version 4:基于随机数生成,不依赖时间戳或网络地址,因此不会暴露硬件信息。

使用场景:

  • 分布式环境下生成全局唯一标识:UUID 适用于需要跨系统或者跨数据库生成唯一 ID 的场景。例如数据库主键、文件唯一标识符、会话 ID 等。
  • 无中心化依赖:UUID 是无状态的,不依赖中心化服务或节点,任何地方都可以生成。
  • 不需要高排序性:UUID 是随机生成的,除了 UUIDv1 基于时间戳之外,UUID 生成的值通常没有明显的顺序,因此在需要有序的 ID 场景下不适用。

优缺点:

  • 优点
    • 简单易用,无需依赖中心化服务。
    • 生成速度快,且跨系统唯一。
    • 容易集成到各种系统和语言中。
  • 缺点
    • 无序性:UUID 通常是随机或基于时间戳生成,不适用于需要顺序递增的场景。
    • 空间占用大:UUID 占用 128 位,相比其他 ID 生成方式比较冗长,占用更多的存储空间。
    • 可读性差:UUID 不直观,无法通过 UUID 判断出生成时间等信息。

2. 雪花算法(Snowflake)

原理:

雪花算法是 Twitter 提出的分布式唯一 ID 生成算法,其产生的 ID 是一个 64 位的长整数,具有局部顺序性,主要由以下几部分组成:

  • 1 bit:符号位,永远为 0。
  • 41 bits:时间戳,单位是毫秒,可以表示约 69 年的时间。
  • 10 bits:机器标识符,用来区分不同的节点,理论上可以支持 1024 台节点。
  • 12 bits:序列号,用来区分同一时间内在同一个节点生成的多个 ID,最多支持每毫秒生成 4096 个唯一 ID。

使用场景:

  • 分布式系统下有序 ID 生成:雪花算法适用于需要在分布式环境中生成全局唯一且趋势递增的 ID,例如订单号、用户 ID、日志 ID 等。
  • 高并发场景:在微服务或高并发的系统中,雪花算法可以快速生成唯一且有序的 ID,不会出现冲突。
  • 需按时间排序的场景:由于雪花算法生成的 ID 包含时间戳,生成的 ID 是大致有序的,适用于需要按生成时间排序的场景。

优缺点:

  • 优点
    • 局部有序:生成的 ID 基本上是按时间顺序递增的,适用于需要排序的场景。
    • 生成速度快:雪花算法在每毫秒内能生成 4096 个 ID,适合高并发环境。
    • 空间占用小:相比 UUID,雪花算法生成的 ID 占用 64 位,比 UUID 的 128 位少很多,更节省存储空间。
    • 包含时间信息:通过 ID 可以反推出生成时间。
  • 缺点
    • 依赖中心化时钟:时间戳是生成 ID 的基础,如果时钟回拨或者服务器时间同步出现问题,可能会导致 ID 冲突或重复。
    • 需要分布式环境配置:需要每个节点都有唯一的机器标识符,分布式配置比较复杂。
    • 无法跨平台唯一:不同系统或平台间生成的 ID 无法保证唯一性,通常在单一分布式环境下使用。

3. 总结与使用场景对比

| 特性 | UUID | 雪花算法(Snowflake) | |——————–|————————————————|——————————————–| | 生成方式 | 随机或基于时间戳、MAC 地址 | 基于时间戳、机器标识符、序列号生成 | | 长度 | 128 位(36 个字符) | 64 位(长整数) | | 有序性 | 通常无序(UUIDv1 有局部时间顺序) | 按时间趋势递增 | | 存储空间 | 大,占用更多存储 | 较小,占用较少存储 | | 并发支持 | 并发生成性能不及雪花算法 | 高并发下支持较好,每毫秒可生成 4096 个 ID | | 依赖环境 | 无依赖,跨系统使用 | 需要分布式环境配置,依赖机器标识符和时间戳 | | 使用场景 | 适用于跨系统、跨平台唯一标识,如数据库主键、会话 ID | 适用于分布式系统内的唯一 ID,如订单号、日志 ID | | 可读性 | 差,无法从 ID 中解读出信息 | 可以解读生成时间信息 |

4. 推荐使用场景

  • UUID:适用于分布式系统或跨平台需要生成唯一标识的场景,尤其是当不需要有序性时,如数据库主键、文件名、会话 ID 等。
  • 雪花算法:适用于需要高并发且需要按时间排序的场景,如订单号生成、日志 ID、分布式系统中的唯一标识。

    8. 如何生成雪花算法?

    在 Java 中,常见的雪花算法实现有多种开源库,如 Twitter 官方的 Snowflake 算法实现,以及基于其改进的其他实现(如 Hutool 和 Baidu 的 UidGenerator)。这些实现的原理相同,主要是在分布式系统中生成唯一、趋势递增的 64 位 ID。下面总结几种常见的雪花算法生成方式及对应代码案例。

1. Twitter Snowflake 官方实现

Twitter 的雪花算法是一种经典的实现,它使用 64 位的长整数来生成唯一 ID。

核心原理

  • 1 bit 符号位,固定为 0。
  • 41 bits 时间戳(毫秒级),表示当前时间相对于某个基准时间的偏移量。
  • 10 bits 机器标识符,用来区分不同的机器(可以支持 1024 台机器)。
  • 12 bits 序列号,用于同一毫秒内的 ID 序列,最多支持每毫秒 4096 个 ID。

实现代码

public class SnowflakeIdGenerator {
    private final long twepoch = 1288834974657L; // 起始时间戳(2021-01-01)

    private final long workerIdBits = 5L; // 机器 ID 所占的位数
    private final long datacenterIdBits = 5L; // 数据中心 ID 所占的位数
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits); // 支持的最大机器 ID
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 支持的最大数据中心 ID
    private final long sequenceBits = 12L; // 序列在 ID 中占的位数

    private final long workerIdShift = sequenceBits; // 机器 ID 左移 12 位
    private final long datacenterIdShift = sequenceBits + workerIdBits; // 数据中心 ID 左移 17 位
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; // 时间戳左移 22 位
    private final long sequenceMask = -1L ^ (-1L << sequenceBits); // 用于序列的掩码

    private long workerId; // 机器 ID
    private long datacenterId; // 数据中心 ID
    private long sequence = 0L; // 毫秒内序列
    private long lastTimestamp = -1L; // 上次生成 ID 的时间戳

    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 for " + (lastTimestamp - timestamp) + " milliseconds");
        }
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask; // 同一毫秒内生成的 ID 数量超过最大值,进行循环
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp); // 等待下一毫秒
            }
        } else {
            sequence = 0L; // 重置序列号
        }
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
    }

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

    protected long timeGen() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        SnowflakeIdGenerator idWorker = new SnowflakeIdGenerator(1, 1);
        for (int i = 0; i < 10; i++) {
            long id = idWorker.nextId();
            System.out.println(id);
        }
    }
}

2. Hutool 中的雪花算法实现

Hutool 是一个 Java 工具类库,其中包含了对 Twitter 雪花算法的封装。它使得 ID 生成更简单易用。

实现代码

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;

public class HutoolSnowflakeExample {
    public static void main(String[] args) {
        // 创建 Snowflake 实例,参数是数据中心 ID 和机器 ID
        Snowflake snowflake = IdUtil.getSnowflake(1, 1);

        for (int i = 0; i < 10; i++) {
            long id = snowflake.nextId();
            System.out.println(id);
        }
    }
}

在 Hutool 中,IdUtil.getSnowflake(workerId, datacenterId) 用于获取一个雪花算法生成器实例,并且生成的 ID 也是 64 位长整数。

3. Baidu UidGenerator

Baidu UidGenerator 是百度开源的高性能全局唯一 ID 生成器,它不仅支持生成唯一 ID,还可以根据 ID 解析生成时间等信息。它同样基于雪花算法,并进行了性能和功能上的改进。

实现代码

首先,需要引入 Maven 依赖:

<dependency>
    <groupId>com.baidu.fsg</groupId>
    <artifactId>uid-generator</artifactId>
    <version>1.0.0</version>
</dependency>

然后配置 workerIdAssigner,并使用生成 ID:

import com.baidu.fsg.uid.UidGenerator;
import com.baidu.fsg.uid.impl.DefaultUidGenerator;
import com.baidu.fsg.uid.worker.WorkerIdAssigner;

public class BaiduUidExample {
    public static void main(String[] args) {
        // 初始化 UID 生成器
        UidGenerator uidGenerator = new DefaultUidGenerator();
        WorkerIdAssigner workerIdAssigner = new WorkerIdAssigner() {
            @Override
            public long assignWorkerId() {
                return 1L; // 返回工作节点 ID
            }
        };
        
        ((DefaultUidGenerator) uidGenerator).setWorkerIdAssigner(workerIdAssigner);

        for (int i = 0; i < 10; i++) {
            long uid = uidGenerator.getUID();
            System.out.println(uid);
            System.out.println(uidGenerator.parseUID(uid)); // 解析 UID 获取生成时间等信息
        }
    }
}

Baidu UidGenerator 支持解析 ID 中的时间、序列号等字段,适用于有高并发需求的分布式系统。

4. 总结

  • Twitter Snowflake:原始的雪花算法实现,适用于需要自定义高并发 ID 生成场景。
  • Hutool 雪花算法:封装了雪花算法,使用简单,适合大多数场景。
  • Baidu UidGenerator:基于雪花算法的改进版本,支持更高并发,且能够解析 ID 中的信息,适合需要详细信息解析和分布式部署的场景。

文档信息

Search

    Table of Contents