「工具」ID生成器

简单生成器

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

/**
 * @version 1.0.0
 * @ClassName IdUtil.java
 * @Description 随机数生成
 */
public final class IdUtil {

    private static final String DATE_PATTERN = "yyyyMMdd";

    private IdUtil() {
    }

    /**
     * 生成18位数字
     * 说明:循环10万左右会有重复ID
     *
     * @return
     */
    public static Long generate18Number() {
        //随机生成一位整数
        int random = (int) (Math.random() * 9 + 1);
        String prefix = String.valueOf(random);
        //格式化日期,生成8位数字
        String middle = LocalDate.now().format(DateTimeFormatter.ofPattern(DATE_PATTERN));
        //生成uuid的hashCode值
        int hashCode = UUID.randomUUID().toString().hashCode();
        //可能为负数
        if (hashCode < 0) {
            hashCode = -hashCode;
        }
        String code = String.valueOf(hashCode);
        if (code.length() > 9) {
            hashCode = Integer.parseInt(code.substring(1));
        }
        String value = prefix + middle + String.format("%09d", hashCode);
        return Long.valueOf(value);
    }
}

基于雪花算法实现

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName TidGenerator.java
 * @Description 可以与Spring结合给该类添加@Component注解,确保只有一个实例
 */
public class TidGenerator {
    /**
     * 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
     */
    private final static long twepoch = 1626923889730L;

    /**
     * 机器标识位数
     */
    private final static long workerIdBits = 5L;
    /**
     * 数据中心标识位数
     */
    private final static long dataCenterIdBits = 5L;
    /**
     * 机器ID最大值
     */
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    /**
     * 数据中心ID最大值
     */
    private final static long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);
    /**
     * 毫秒内自增位
     */
    private final static long sequenceBits = 12L;
    /**
     * 机器ID偏左移12位
     */
    private final static long workerIdShift = sequenceBits;
    /**
     * 数据中心ID左移17位
     */
    private final static long dataCenterIdShift = sequenceBits + workerIdBits;
    /**
     * 时间毫秒左移22位
     */
    private final static long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;

    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
    /**
     * 上次生产id时间戳
     */
    private static long lastTimestamp = -1L;
    /**
     * 0,并发控制
     */
    private long sequence = 0L;

    private final long workerId;
    /**
     * 数据标识id部分
     */
    private final long dataCenterId;

    /**
     * 默认的TID长度
     */
    private final static int defaultTidLength = 12;

    private static final String SPECIAL_CHARS = "!@#$%^&*_=+-/";

    public TidGenerator() {
        this.dataCenterId = getDataCenterId(maxDataCenterId);
        this.workerId = getMaxWorkerId(dataCenterId, maxWorkerId);
    }

    /**
     * @param workerId     工作机器ID
     * @param dataCenterId 序列号
     */
    public TidGenerator(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("data center Id can't be greater than %d or less than 0", maxDataCenterId));
        }
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
    }

    /**
     * 获取下一个ID
     *
     * @return
     */
    public synchronized long nextSequenceId() {
        long timestamp = timeGen();

        //检查是否出现了「时钟回拨」问题
        timestamp = checkClockMovedBackwards(timestamp);

        if (lastTimestamp == timestamp) {
            // 当前毫秒内,则+1
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 当前毫秒内计数满了,则等待下一秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // ID偏移组合生成最终的ID,并返回ID
        long nextId = ((timestamp - twepoch) << timestampLeftShift)
                | (dataCenterId << dataCenterIdShift)
                | (workerId << workerIdShift) | sequence;

        return nextId;
    }

    /**
     * 产生下一个TID
     */
    public synchronized String nextTid() {
        //1. 生成一个有序序列
        long sequence = nextSequenceId();
        //2. 转化为62进制
        String str = this.decimalToSixtyTwo(sequence);
        //3. 补全12位并返回
        return completeDigits(str, defaultTidLength);
    }

    /**
     * 产生下一个指定长度的TID
     */
    public synchronized String nextTid(int length) {
        //1. 生成一个有序序列
        long sequence = nextSequenceId();
        //2. 转化为62进制
        String str = this.decimalToSixtyTwo(sequence);
        //3. 补全length位并返回
        return completeDigits(str, length);
    }

    /**
     * 获取至少18位的整数
     *
     * @return
     */
    public synchronized long getAtLeast18BitSequenceId() {
        long seqId = this.nextSequenceId();
        String seq = String.valueOf(seqId);
        if (seq.length() >= 18) {
            return seqId;
        }
        if (seq.length() < 18) {
            //补全位数
            //随机生成一位整数
            int random = (int) (Math.random() * 9 + 1);
            String prefix = String.valueOf(random);
            String value = prefix + String.format("%017d", seqId);
            return Long.parseLong(value);
        }
        return seqId;
    }

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

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

    /**
     * 获取 maxWorkerId
     */
    protected static long getMaxWorkerId(long dataCenterId, long maxWorkerId) {
        StringBuffer mpId = new StringBuffer();
        mpId.append(dataCenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (!name.isEmpty()) {
            /*
             * GET jvmPid
             */
            mpId.append(name.split("@")[0]);
        }
        /*
         * MAC + PID 的 hashcode 获取16个低位
         */
        return (mpId.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * 数据标识id部分
     */
    protected static long getDataCenterId(long maxDataCenterId) {
        long id = 0L;
        try {
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            if (network == null) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                id = ((0x000000FF & (long) mac[mac.length - 1])
                        | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                id = id % (maxDataCenterId + 1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return id;
    }

    /**
     * 检查是否出现了「时钟回拨」问题
     */
    private long checkClockMovedBackwards(long nowStamp) {
        if (nowStamp >= lastTimestamp) {
            return nowStamp;
        }

        //如果因为某些原因出现了「时钟回拨」问题
        long diff = lastTimestamp - nowStamp;

        //如果时间回拨超过1秒钟,则对外抛出异常,否则暂停1秒钟后再次尝试获取
        if (diff >= TimeUnit.SECONDS.toMillis(1)) {
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", diff));
        }

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException("An error occurred here", e);
        }

        nowStamp = timeGen();
        if (nowStamp >= lastTimestamp) {
            return nowStamp;
        }

        //如果还出现这个问题,则抛出异常
        throw new RuntimeException(String.format("Clock moved backwards again. Refusing to generate id for %d milliseconds", (lastTimestamp - nowStamp)));
    }

    /**
     * 补全字符串位数(最后返回length位)
     *
     * @param str 原始字符串
     * @return java.lang.String
     */
    protected String completeDigits(String str, int length) {
        if (str.length() >= length) {
            return str;
        }
        int diff = length - str.length() - 1;
        //长度标识 + 1位的随机数(可能不存在) + 原递增序列
        return this.decimalToSixtyTwo(str.length()) + this.randomChars(diff, false) + str;
    }

    /**
     * 十进制转62进制(仅限正整数)
     *
     * @param num 十进制数字
     * @return java.lang.String
     */
    protected String decimalToSixtyTwo(long num) {
        if (num <= 0) {
            return "0";
        }

        StringBuilder sb = new StringBuilder();
        //余数
        long remainder;

        while (num > 0) {
            remainder = num % 62;

            //0-9
            if (remainder < 10) {
                sb.append((char) ('0' + remainder));
            }
            //A-Z
            else if (remainder < 36) {
                sb.append((char) ('A' + remainder - 10));
            }
            //a-z
            else {
                sb.append((char) ('a' + remainder - 36));
            }

            num = num / 62;
        }

        //因为在上面的循环过程中,后一次循环本应是计算出来的高位字符,但是却被我们放在字符串的最后面,因此最终结果需要再反转一次
        return sb.reverse().toString();
    }

    /**
     * 生成指定位数的随机数
     *
     * @param length              长度
     * @param containSpecialChars 是否包含特殊字符
     */
    protected String randomChars(int length, boolean containSpecialChars) {
        char[] chars = new char[length];
        Random rnd = new Random();

        //1. 填补空白位置的字符
        for (int i = 0; i < length; i++) {
            if (chars[i] == 0) {
                chars[i] = (containSpecialChars ? nextCharWithSpecialChars(rnd) : nextChar(rnd));
            }
        }

        //2. 返回结果
        return new String(chars);
    }

    /**
     * 返回一个随机的字符(包含特殊字符)
     */
    private char nextCharWithSpecialChars(Random rnd) {
        switch (rnd.nextInt(4)) {
            case 0:
                return (char) ('a' + rnd.nextInt(26));
            case 1:
                return (char) ('A' + rnd.nextInt(26));
            case 2:
                return (char) ('0' + rnd.nextInt(10));
            default:
                return SPECIAL_CHARS.charAt(rnd.nextInt(SPECIAL_CHARS.length()));
        }
    }

    /**
     * 返回一个随机的字符
     */
    private char nextChar(Random rnd) {
        switch (rnd.nextInt(3)) {
            case 0:
                return (char) ('a' + rnd.nextInt(26));
            case 1:
                return (char) ('A' + rnd.nextInt(26));
            default:
                return (char) ('0' + rnd.nextInt(10));
        }
    }
}

 

上一篇:手抄冒泡排序[C语言版本]


下一篇:mt19937