这期工作有个需求,写个公文编号生成服务,提供公文编号生成的功能,给其他服务调用。

要求,10位字符串,前2位是英文字母,后8位是数字。原来系统里也是有编号生成器的,但是实现逻辑不大符合要求,数字位部分包含了年月日信息,以及递增数字部分。也不是说这样的方式不行,但是比较浪费,不过编号中也包含了部分业务、时间信息。

而对接的系统表示,他们的号码可用范围空间已经不多了,想要继续维持可持续的使用需要扩展编号字段长度,但是他们现在不会去改扩展这个长度,当前这个是一个老系统,需要修改的地方涉及很多,有风险。且他们已经在开发新系统了,预期需要半年左右可以接入,当前只能给我们分配一个新的前2位英文字母的区段给我们的3个系统一起使用。

好吧,为了不浪费,且为了照顾后期编号长度拓展的需求于是有了这个公文编号生成服务的开发需求。实现起来也很简单,一般通用常规作法,需要有一个有原子性,且稳定递增的计数器,这个时候就有两个选择DB主键自增,或者Redis.inc方法来实现最终返回一个Long型数值。这也没啥纠结的直接redis了。

接下来就是数字转字符串,并且需要前面补0到指定长度,直接上代码了

public  static String charArrFunc(int num, int numLength, String prefix){
        String numStr = String.valueOf(num);
        char[] chars = numStr.toCharArray();
        char[] defaultArr = new char[numLength+prefix.length()];
        Arrays.fill(defaultArr,'0');
        for (int i = 0; i < prefix.length(); i++) {
            defaultArr[i] = prefix.charAt(i);
        }
        int dif = numLength - chars.length + prefix.length();
        System.arraycopy(chars, 0, defaultArr, dif, chars.length);
        return new String(defaultArr);
    }

思路也很简单,生成指定长度的char[],全部填上0,在需要的位置填上需要的值,最后new String。写完之后自测了下,没毛病。但是我想了下,是不是还有其他方法,包括项目代码里原来也有类似的编码补0的现成方法,原来现成的方法是这样的

 public static String autoCompleteCode(String code, int num) {
        if (StringUtils.isEmpty(code)) {
            return "";
        }
        // 0代表前面补充0 d代表参数为正整数型
        return String.format("%0" + num + "d", Long.parseLong(code));
    }

好像很简单,调用String.format,按照指定的格式来转换,到源码里看下是调用

new Formatter().format(format, args).toString()

来实现的,而再往下看也是很明显的使用了Pattern,性能必然不能相比。然后又搜了下,网上一般还有一种方法

public static String TEMPLATE_ZERO = "00000000";
    public static String startZero(int num, String prefix){
        DecimalFormat g1 = new DecimalFormat(TEMPLATE_ZERO);
        return prefix+g1.format(num);
    }

使用DecimalFormat来实现,基本网上看别人写的大多数都是上面这两种,可能稍微有一点变化。不过性能和自己写了操作char[]来比应该还是不能比的。但是从易用性,便捷开发的角度来讲,这两种方法是十分方便使用的。

索性自己写个Test来试下看看吧。


import org.apache.commons.lang3.StringUtils;

import java.text.DecimalFormat;
import java.util.Arrays;

/**
 * NumberToStringTest<br>
 *
 * @author CheungQ
 * @date 2021/7/20 9:19
 */
public class NumberToStringTest {


    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(NumberToStringTest::case1);
            thread.start();
        }
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(NumberToStringTest::case2);
            thread.start();
        }
//        for (int i = 0; i < 10; i++) {
//            Thread thread = new Thread(NumberToStringTest::case4);
//            thread.start();
//        }
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(NumberToStringTest::case3);
            thread.start();
        }
    }


    public static void case1(){
        long start = System.currentTimeMillis();
        for (int i = 0; i < 99999999; i++) {
           String str = "XX"+autoCompleteCode(String.valueOf(i),8);
        }
        System.out.println("case1:"+(System.currentTimeMillis()-start));
    }
    public static String autoCompleteCode(String code, int num) {
        if (StringUtils.isEmpty(code)) {
            return "";
        }
        // 0代表前面补充0 d代表参数为正整数型
        return String.format("%0" + num + "d", Long.parseLong(code));
    }


//    @Test
    public static void case2(){
        long start = System.currentTimeMillis();
        for (int i = 0; i < 99999999; i++) {
            String str = charArrFunc(i,8,"XX");
            System.out.println(str);
        }
        System.out.println("case2:"+(System.currentTimeMillis()-start));
    }



    public  static String charArrFunc(int num, int numLength, String prefix){
        String numStr = String.valueOf(num);
        char[] chars = numStr.toCharArray();
        char[] defaultArr = new char[numLength+prefix.length()];
        Arrays.fill(defaultArr,'0');
        for (int i = 0; i < prefix.length(); i++) {
            defaultArr[i] = prefix.charAt(i);
        }
        int dif = numLength - chars.length + prefix.length();
        System.arraycopy(chars, 0, defaultArr, dif, chars.length);
        return new String(defaultArr);
    }


    public static void case3(){
        long start = System.currentTimeMillis();
        for (int i = 0; i < 99999999; i++) {
            String str = "XX"+charArrFunNonPre(i,8);
        }
        System.out.println("case3:"+(System.currentTimeMillis()-start));
    }
    public static String charArrFunNonPre(int num, int numLength){
        String numStr = String.valueOf(num);
        char[] chars = numStr.toCharArray();
        char[] defaultArr = new char[numLength];
        Arrays.fill(defaultArr,'0');
        int dif = numLength - chars.length ;
        System.arraycopy(chars, 0, defaultArr, dif, chars.length);
        return new String(defaultArr);
    }




    public static void case4(){
        long start = System.currentTimeMillis();
        for (int i = 0; i < 99999999; i++) {
            String str = startZero(i,"XX");
        }
        System.out.println("case4:"+(System.currentTimeMillis()-start));
    }

    public static String TEMPLATE_ZERO = "00000000";
    public static String startZero(int num, String prefix){
        DecimalFormat g1 = new DecimalFormat(TEMPLATE_ZERO);
        return prefix+g1.format(num);
    }
}

从0跑到99999999,一共1亿个数字,跑10次就是10亿次,结果大概是这样

case2:29825
case2:29801
case2:30019
case2:30070
case2:30194
case2:30377
case2:30655
case2:30991
case2:31659
case2:31703
case1:175303
case1:176396
case1:177642
case1:178548
case1:189006
case1:189455
case1:189635
case1:190119
case1:190529
case1:190583
case4:124600
case4:125209
case4:125537
case4:125694
case4:125819
case4:127902
case4:128627
case4:128940
case4:129393
case4:129408

case2,就是自己写的方法,执行时间大概在3000~3200

case1,String.format的方法,执行时间在17500~19000

case3,DecimalFormat的方法,执行时间在12500~13000

时间差距还是比较明显的,具体数值根据个人电脑配置跑起来会有所不同,另外执行的时候因为是直接开了多线程全速在跑,cpu占用会打到100%。

其实也就是稍微闲了一点自己写了个方法。