Java 生成任意位数永不重复的随机数策略实现

在项目中遇到客户一个需求,ID的生成策略:长度8位,用户在知道自己的ID(如:10000018)后,不能通过ID来推测相关信息,如用户可能推测我是第18个注册用户,上一个注册用户的ID是10000017,这样就不能很好的保护账号安全了,可以凭证推测数据来进行不法之事。

经过网上查找和思考,便有了如下的解决方案:

  • 生成一个装有1-99999999数值的数组
  • 将数值内的值随机打乱
  • 将数值的值分开保存到不同的文件(txt)中
  • 获取ID的时候,可以随机从任意一文件中获取第一行的ID值,然后将剩余行的ID值重写写会原文件
  • 如果文件中的ID值已经取完则删除

逻辑很简单,就看实现了,代码如下:

生成ID并保存到硬盘

    /**
     * 生成1-99999999的数字编码保存到硬盘
     */
    private static void generateAppCode2Disk(){
        int begin = 1;  
        int end = 99999999;  
        int count = begin + end;  
        //生成1到99999999的所有整数  
        int[] codes = new int[count + 1];  
        for (int i = begin; i <= end; i++){  
            codes[i] = i;  
        }
        //随机交换数据  
        int index = 0;
        int tempCode = 0;
        Random random = new Random();
        for (int i = begin; i <= end; i++){  
            index = random.nextInt(count+1);  
            tempCode = codes[index];  
            codes[index] = codes[i];  
            codes[i] = tempCode;  
        }
        //生成1000个文件,每个文件包含100000个appCode
        StringBuilder sb = new StringBuilder();
        int flag = 100000;
        System.out.println("***********开始**********");
        try {
            for(int i = begin; i <= end; i++){
                sb.append(codes[i]).append("\n");
                if(i == end || i%flag == 0){
                    File folder = new File("D:/IDGenerate");
                    if(!folder.isDirectory()){
                        folder.mkdir();
                    }
                    if(i==end){
                        i = end +1;
                    }
                    File file = new File("D:/IDGenerate/ID_"+(i/flag)+".txt");
                    if (!file.exists()) {
                        file.createNewFile();
                    }
                    BufferedWriter bw=new BufferedWriter(new FileWriter(file.getAbsoluteFile()));
                    bw.write(sb.toString());
                    bw.flush();
                    bw.close();
                    sb = new StringBuilder();
                    System.out.println("当前i值:"+i+"第"+(i/flag)+"个文件生成成功!");
                }
            }
            System.out.println("***********结束**********");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

获取ID

    //获取唯一8位ID
    public String createAppCode(){
        BufferedWriter bw = null;
        BufferedReader br = null;
        FileReader fr = null;
        FileWriter fw = null;
        try {
            String dir = "D:/";
            if(StringUtils.isBlank(dir)){
                throw new Exception("获取文件路径为空");
            }
            File rootFile = new File(dir);
            String[] fileNames = rootFile.list();
            if(fileNames == null || fileNames.length == 0){
                throw new Exception("路径不正确,或者ID已经分配完毕,请联系管理员");
            }
            //获取第一个文件
            fr=new FileReader(dir+"/"+fileNames[0]);//获取文件流
            br = new BufferedReader(fr); //将流整体读取。
            StringBuilder sb = new StringBuilder();
            String appCode = "";
            String temp;
            int count =1;
            while(!StringUtils.isBlank(temp=br.readLine())){
                if(count == 1){
                    count++;
                    appCode = temp;
                    continue;
                }
                else{
                    sb.append(temp).append("\n");
                }

            }
            br.close();
            fr.close();
            if(!StringUtils.isBlank(appCode)){
                //判断文件内容是否还有下一行
                if(sb.length()<=0){
                    File delFile = new File(dir+"/"+fileNames[0]);
                    if(delFile.exists()){
                        delFile.delete();//删掉
                    }
                }
                else{
                    //将剩余内容重写写回文件
                    fw = new FileWriter(dir+"/"+fileNames[0]);
                    bw=new BufferedWriter(fw);
                    bw.write(sb.toString());
                    bw.flush();
                    bw.close();
                    fw.close();
                }
                String prex = "00000000";
                appCode = prex.substring(0,prex.length()-appCode.length())+appCode;
                return appCode;
            }
            else{
                throw new Exception("文件中内容为空");
            }
        } catch (Exception e) {
            log.error("获取ID error:"+e.getMessage());
            return null;
        } finally{
            try {
                if(bw != null)bw.close();
                if(br != null)bw.close();
                if(fr != null)bw.close();
                if(fw != null)bw.close();
            } catch (IOException e) {
                log.error("关闭文件流文件异常:"+e.getMessage());
            }
        }
    }

这样将生成的ID分成1000个文件保存到硬盘,取数据的时候从任意文件中取都可,这样能很好的保证了随机,不重复,而且在测试的时候发现效率还不错,内存,CPU的使用都微乎其微。

为了更好的优化获取ID效率,可以有如下改进:

  • 将ID保存到更多的文件中,这样文件的容量就可以很小,加载的时候就更快
  • 预读ID,可以提前预读50,100等数量的ID保存到list中,直接从内存取数据就更快了,不过这逻辑就复杂了很多,要保证list的数据和硬盘数据的同步,就需要更多的代码来判断了

    此文章供大家参考,大家有什么好的建议,算法都可以提出来交流的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值