需求
流水号生成的一种算法。
需求:生成指定位数的流水号,如:生成6位流水号,从A00001、A00002一直递增到ZZZZZZ结束。
000000 -> 999999
A00000 -> A99999
B00000 -> B99999
……
Z00000 -> Z99999
ZA0000 -> ZA9999 ->ZZZZZZ
算法介绍
- 定义一个可选字母的数组A-Z;
private static final Character[] CHARS = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
在这里可以去掉一些和数字长相冲突的字母,比如O.
-
定义静态代码块,计算出A00000 . AA0000、AAA000、AAAA00、AAAAA0、AAAAAA的数字;
private static final int CHAR_LEN; private static final String MAX; private static final Long[] BOUNDARIES; static { CHAR_LEN = CHARS.length; Character lastChar = CHARS[CHAR_LEN - 1]; MAX = StringUtils.leftPad(String.valueOf(lastChar), DEFALUT_LEN, lastChar); BOUNDARIES = new Long[DEFALUT_LEN + 2]; Long last = 0L; BOUNDARIES[0] = 0L; for (int i = 1; i <= DEFALUT_LEN + 1; i++) { int t = i - 1; BOUNDARIES[i] = last + (long) Math.pow(10, DEFALUT_LEN - t) * (long) Math.pow(CHAR_LEN, (i - 1)); last = BOUNDARIES[i]; } log.info("code generator boundaries: " + Arrays.toString(BOUNDARIES)); }
-
如果目标数字大于等于BOUNDARIES的最后一位,也就是超出了我们定义的最大长度。抛出异常;
if (num >= BOUNDARIES[DEFALUT_LEN + 1]) { throw new RuntimeException("the code is has reached it's maximum value [" + MAX + "]"); }
-
获取目标数字是在000000、A00000 、AA0000、AAA000、AAAA00、AAAAA0、AAAAAA这些区间的哪一段。
int initialIndex = Arrays.binarySearch(BOUNDARIES, num);
-
计算出所在区间的步长(从一个字母到下一个字母所经过的数字,如A00000到AA0000中的步长 ,也就是A00000到B00000 的步长就是(3600000-1000000)/26 这里26是上面定义的字母数组的长度 ),然后开始循环,从一步获取的索引位置开始,到BOUNDARIES数组结束;
-
在循环中重新计算出目标数字所在的区间及步长。
完整代码
import lombok.extern.java.Log;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
/**
* @Author: Eynait
* @Date: 2023/7/28 16:18
*/
@Log
public class Coder {
private static final int DEFALUT_LEN = 6;
private static final Character[] CHARS = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
private static final int CHAR_LEN;
private static final String MAX;
private static final Long[] BOUNDARIES;
static {
CHAR_LEN = CHARS.length;
Character lastChar = CHARS[CHAR_LEN - 1];
MAX = StringUtils.leftPad(String.valueOf(lastChar), DEFALUT_LEN, lastChar);
BOUNDARIES = new Long[DEFALUT_LEN + 2];
Long last = 0L;
BOUNDARIES[0] = 0L;
for (int i = 1; i <= DEFALUT_LEN + 1; i++) {
int t = i - 1;
BOUNDARIES[i] = last + (long) Math.pow(10, DEFALUT_LEN - t) * (long) Math.pow(CHAR_LEN, (i - 1));
last = BOUNDARIES[i];
}
log.info("code generator boundaries: " + Arrays.toString(BOUNDARIES));
}
public static String generator(long num) {
if (num >= BOUNDARIES[DEFALUT_LEN + 1]) {
throw new RuntimeException("the code is has reached it's maximum value [" + MAX + "]");
}
int initialIndex = Arrays.binarySearch(BOUNDARIES, num);
if (initialIndex < 0) {
initialIndex = -(initialIndex + 2);
}
if (initialIndex == 0) {
return StringUtils.leftPad(String.valueOf(num), DEFALUT_LEN, "0");
}
StringBuffer result = new StringBuffer();
Long boundary = BOUNDARIES[initialIndex];
long step = (BOUNDARIES[initialIndex + 1] - boundary) / CHAR_LEN;
for (int i = initialIndex; i > 0; i--) {
if (boundary < CHAR_LEN) {
result.append(num);
break;
}
long diff = num - boundary;
long targetIndex = diff / step;
result.append(CHARS[(int) targetIndex]);
boundary = boundary + step * targetIndex;
step = step / CHAR_LEN;
}
if (initialIndex < DEFALUT_LEN) {
result.append(StringUtils.leftPad(String.valueOf(num - boundary), DEFALUT_LEN - initialIndex, "0"));
}
return result.toString();
}
public static void main(String[] args) {
for (int i = 999999; i < 9999999; i++) {
System.out.println(i + " - " + generator(i));
}
}
}