Java 的 ID 生成器(工具类)

  • Post author:
  • Post category:java


使用业务场景

1. 数据库自增主键无法满足

2. 分布式系统的ID生成

3. 需要按照时间生成唯一ID

4. 无序的唯一ID

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.util.UUID;

/**
 *  用于生产各种各样的ID
 */
public class IdUtils {

    private static String middle = "";

    static {
        middle = MathUtils.makeUpNewData(Math.abs(NetworkUtils.getHostIP().hashCode()) + "", 4) +   //4位IP地址hash
                 MathUtils.makeUpNewData(NetworkUtils.getPid(), 4);                                 //4位PID进程hash
    }

    /**
     * 以毫微秒做基础计数, 返回唯一有序增长ID
     * <pre>System.nanoTime()</pre>
     * <pre>
     *  线程数量:   100
     *  执行次数:   1000
     *  平均耗时:   222 ms
     *  数组长度:   100000
     *  Map Size:   100000 
     * </pre>
     * @return  ID长度32位
     */
    public static String getIncreaseIdByNanoTime(){
        return System.nanoTime()+                                                       //时间戳-14位
               middle+                                                                  //标志-8位
               MathUtils.makeUpNewData(Thread.currentThread().hashCode()+"", 3)+        //3位线程标志
               MathUtils.randomDigitNumber(7);                                          //随机7位数
    }

    /**
     * 以毫秒做基础计数, 返回唯一有序增长ID, 有几率出现线程并发
     * <pre>System.currentTimeMillis()</pre>
     * <pre>
     *  线程数量:   100
     *  执行次数:   1000
     *  平均耗时:   206 ms
     *  数组长度:   100000
     *  Map Size:   99992 
     * </pre>
     * @return  ID长度32位
     */
    public static String getIncreaseIdByCurrentTimeMillis(){
        return  System.currentTimeMillis()+                                             //时间戳-14位
                middle+                                                                 //标志-8位
                MathUtils.makeUpNewData(Thread.currentThread().hashCode()+"", 3)+       //3位线程标志
                MathUtils.randomDigitNumber(8);                                         //随机8位数
    }

    /**
     * 基于UUID+MD5产生唯一无序ID
     * <pre>
     *  线程数量:   100
     *  执行次数:   1000
     *  平均耗时:       591 ms
     *  数组长度:   100000
     *  Map Size:   100000 
     * </pre> 
     * @return  ID长度32位
     */
    public static String getRandomIdByUUID(){
        return DigestUtils.md5Hex(UUID.randomUUID().toString());
    }

    /* ---------------------------------------------分割线------------------------------------------------ */
    /** 字符串MD5处理类 */
    private static class DigestUtils {

        private static final char[] DIGITS_LOWER =
            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

        private static final char[] DIGITS_UPPER =
            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

        private static char[] encodeHex(final byte[] data, final char[] toDigits) {
            final int l = data.length;
            final char[] out = new char[l << 1];
            for (int i = 0, j = 0; i < l; i++) {
                out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
                out[j++] = toDigits[0x0F & data[i]];
            }
            return out;
        }

        public static String md5Hex(String str){
            return md5Hex(str, false);
        }

        public static String md5Hex(String str, boolean isUpper){
            try {
                return new String(encodeHex(MessageDigest.getInstance("MD5").digest(str.getBytes("UTF-8")), isUpper ? DIGITS_UPPER : DIGITS_LOWER));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }


    /* ---------------------------------------------分割线------------------------------------------------ */
    /** 网络相关的处理类  */
    private static class NetworkUtils {

        private static final String DEFAULT_HOST_IP = "10.10.10.10";

        /**
         *  获取当前进程的PID 
         */
        public static String getPid(){
            return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
        }

        /**
         *  获取当前进程的主机IP地址
         */
        public static String getHostIP(){
            try {
                return InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                return DEFAULT_HOST_IP;
            }
        }

    }

    /* ---------------------------------------------分割线------------------------------------------------ */
    /** 数据处理的相关类  */
    private static class MathUtils {

        private static final String DEFAULT_DIGITS =                    "0";
        private static final String FIRST_DEFAULT_DIGITS =              "1";

        /**
         * @param target    目标数字
         * @param length    需要补充到的位数, 补充默认数字[0], 第一位默认补充[1]
         * @return          补充后的结果
         */
        public static String makeUpNewData(String target, int length){
            return makeUpNewData(target, length, DEFAULT_DIGITS);
        }

        /**
         * @param target    目标数字
         * @param length    需要补充到的位数
         * @param add       需要补充的数字, 补充默认数字[0], 第一位默认补充[1]
         * @return          补充后的结果
         */
        public static String makeUpNewData(String target, int length, String add){
            if(target.startsWith("-")) target.replace("-", "");
            if(target.length() >= length) return target.substring(0, length);
            StringBuffer sb = new StringBuffer(FIRST_DEFAULT_DIGITS);
            for (int i = 0; i < length - (1 + target.length()); i++) {
                sb.append(add);
            }
            return sb.append(target).toString();
        }

        /**
         * 生产一个随机的指定位数的字符串数字
         * @param length
         * @return
         */
        public static String randomDigitNumber(int length){
            int start = Integer.parseInt(makeUpNewData("", length));//1000+8999=9999
            int end = Integer.parseInt(makeUpNewData("", length + 1)) - start;//9000
            return (int)(Math.random() * end) + start + "";
        }

    }

}



版权声明:本文为fay_maplemei原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。