【看代码直接跳到最后】
一.需求【数据库不支持字符串自增,需要自己实现】
1.当需要生成一些唯一编号的数据时,通常都是使用uuid作为唯一值,但当编号有上下级,如行政区划码,物品分类树,多级目录分类等
2.我们只拿到某一给节点时,需要快速解析出该节点的所有父级数据,以及子集数据聚合时,就需要用到一个在该节点唯一的编码,
3.如已知区划码【510107】,我们即可一下子知道这个区划码对应的是【51:四川,01:成都,07:武侯区】,要查询【510107】下面的数据,只需要在sql中 加一个【code like '510107%'】条件即可
我们在添加这种多层级,由要求能快速统计分析的数据时就需要为每一条数据生成唯一的code,并且通过这个code可直观的看出层级关系,要保证唯一,并使code精简只能通过code的自增才能同时满足这两个条件
二.常见方案
- 通过数字解析实现code自增;
- 局限性:
- 需要的字符太长时解析为数字任意超出长度限制
- 只支持数字操作,当需要加入大小写字母时则不支持
- 局限性:
- 直接使用uuid;
- 局限性:
- 每一级的code都太长,不够精简
- 当层级太多时,子级的code长度将难以接受
- 局限性:
- 使用随机字符;【最容易实现,但一定会重复】
三.解决办法
通过自定义字符串自增实现code的生成;
如下为两个数字相加的代码:
private static String numberAdd(String d1, String d2) {
int l1 = d1.length() - 1, l2 = d2.length() - 1;
int t = 0;//用于记录是否进位的变量
StringBuilder s = new StringBuilder();
while (l1 >= 0 || l2 >= 0 || t > 0) {//当d1或d2没有遍历完,或者有需要进位的数字,则进行循环
if (l1 >= 0) t += d1.charAt(l1--) - '0';//t=t+d1的当前位数字
if (l2 >= 0) t += d2.charAt(l2--) - '0';//t=t+d2的当前位数字
s.append(t % 10);//取出t的个位加到字符串上面,最早得到如81,891,8991
t /= 10;//t舍去个位的数字
}
return s.reverse().toString();//将s反转即可得到两个数相加后的数字
}
基于上述字符相加的基本规则,很容易得到大写字母相加,小写字母相加,数字/大写字母/小写字母组合相加的代码
有了字符串相加的代码,自增将是+1操作,很容易实现【具体代码不再赘述,文章最后粘贴有】
四.测试效果
@Test
public void test1() {
System.out.println(numberAdd("9", "9"));//18
System.out.println(numberAdd("99", "99"));//198
System.out.println(numberAdd("9999", "9999"));//19998
System.out.println(numberAdd("99999999", "99999999"));//199999998
System.out.println(numberAdd("9999999999999999", "9999999999999999"));//19999999999999998
System.out.println(numberAdd("99999999999999999999999999999999", "99999999999999999999999999999999"));//199999999999999999999999999999998
System.out.println(numberAdd("9999999999999999999999999999999999999999999999999999999999999999", "9999999999999999999999999999999999999999999999999999999999999999"));//19999999999999999999999999999999999999999999999999999999999999998
System.out.println(numberAdd("99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"));//199999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999998
System.out.println(numberAdd("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"));//19999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999998
System.out.println(numberAdd("99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"));//199999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999998
}
五.源代码【数字,大写字母,小写字母,数字/大小写字母组合自增的代码】
注意,下面代码中使用了hutool的字符串工具,可替换为其他字符串工具
package com.wanger.utils.str;
import cn.hutool.core.util.StrUtil;
/**
* @Author: wanger
* @Date: 2023/10/24 16:32
* @Description: 字符串相加与自增工具,支持数字相加,大写字母相加,小写字母相加,数字大小写字母相加,以及对应的自增操作
*/
public class CodeUtils {
/**
* 通用单一字符串相加,如大写字母,小写字母,数字等
*
* @param d1 字符串1
* @param d2 字符串2
* @param start 起始字符,如数据的为0
* @param stepSize 涉及到的步长,如数字的为10
* @return 返回相加后的字符串
*/
private static String characterAddition(String d1, String d2, char start, int stepSize) {
int i = d1.length() - 1, j = d2.length() - 1, d = 0;
StringBuilder s = new StringBuilder();
while (i >= 0 || j >= 0 || d > 0) {
if (i >= 0) d += d1.charAt(i--) - start;
if (j >= 0) d += d2.charAt(j--) - start;
s.append((char) (d % stepSize + start));
d /= stepSize;
}
return s.reverse().toString();
}
/**
* 数字相加操作
*
* @param d1 数字1
* @param d2 数字2
* @return 返回相加后的数字
*/
public static String digitAddition(String d1, String d2) {
if (StrUtil.isNumeric(d1) && StrUtil.isNumeric(d2)) {
return characterAddition(d1, d2, '0', 10);
} else throw new RuntimeException("数字相加的字符串内容只能是数字d1=" + d1 + ",d2=" + d2);
}
/**
* 大写字母相加,注意A对应十进制的0所以A+A=A
*
* @param d1 大写字母1
* @param d2 大写字母2
* @return 相加后的大写字母
*/
public static String upperCaseAddition(String d1, String d2) {
if (StrUtil.isUpperCase(d1) && StrUtil.isUpperCase(d2)) {
return characterAddition(d1, d2, 'A', 26);
} else throw new RuntimeException("大写字母相加的字符串内容只能是大写字母d1=" + d1 + ",d2=" + d2);
}
/**
* 小写字母相加,注意a对应十进制的0所以a+a=a
*
* @param d1 小写字母1
* @param d2 小写字母2
* @return 相加后的小写字母
*/
public static String lowerCaseAddition(String d1, String d2) {
if (StrUtil.isLowerCase(d1) && StrUtil.isLowerCase(d2)) {
return characterAddition(d1, d2, 'a', 26);
} else throw new RuntimeException("小写字母相加的字符串内容只能是小写字母d1=" + d1 + ",d2=" + d2);
}
/**
* 数字自增
*
* @param digit 要自增的数字
* @param limitLength 长度限制
* @return 返回自增后的数字
*/
public static String digitalIncrement(String digit, int limitLength) {
return keepSpecifiedLength(digitAddition(digit, "1"), '0', limitLength);
}
/**
* 数字自增
*
* @param digit 要自增的数字
* @return 返回自增后的数字
*/
public static String digitalIncrement(String digit) {
return digitalIncrement(digit, -1);
}
/**
* 大写字母自增
*
* @param digit 要自增的大写字母
* @param limitLength 长度限制
* @return 返回自增后的大写字母
*/
public static String upperCaseIncrement(String digit, int limitLength) {
return keepSpecifiedLength(upperCaseAddition(digit, "B"), 'A', limitLength);
}
/**
* 大写字母自增
*
* @param digit 要自增的大写字母
* @return 返回自增后的大写字母
*/
public static String upperCaseIncrement(String digit) {
return upperCaseIncrement(digit, -1);
}
/**
* 小写字母自增
*
* @param digit 要自增的小写字母
* @param limitLength 长度限制
* @return 返回自增后的小写字母
*/
public static String lowerCaseIncrement(String digit, int limitLength) {
return keepSpecifiedLength(lowerCaseAddition(digit, "b"), 'a', limitLength);
}
/**
* 小写字母自增
*
* @param digit 要自增的小写字母
* @return 返回自增后的小写字母
*/
public static String lowerCaseIncrement(String digit) {
return lowerCaseIncrement(digit, -1);
}
/**
* 数字,大写字母,小写字母组合相加,如9+1=A,Z+1=a,z+1=10
*
* @param d1 字符串1
* @param d2 字符串2
* @return 返回相加后的字符串
*/
public static String characterAddition(String d1, String d2) {
if (d1 == null) d1 = "";
if (d2 == null) d2 = "";
numericUpperAndLowerCaseLetterDetermination(d1);
numericUpperAndLowerCaseLetterDetermination(d2);
int i = d1.length() - 1, j = d2.length() - 1, d = 0;
StringBuilder s = new StringBuilder();
while (i >= 0 || j >= 0 || d > 0) {
if (i >= 0) d += characterDifference(d1.charAt(i--));
if (j >= 0) d += characterDifference(d2.charAt(j--));
s.append(characterRestore(d));
d /= 62;
}
return s.reverse().toString();
}
/**
* 对字符串进行前缀补0或删除前缀实现使其保持指定的长度
*
* @param str 要进行规整的字符串
* @param prefixCharacter 要补充的前缀字符
* @param limitLength 指定的长度,-1表示不进行长度限制
* @return 返回规整后的字符串
*/
private static String keepSpecifiedLength(String str, char prefixCharacter, int limitLength) {
StringBuilder s = new StringBuilder(new StringBuilder(str).reverse());
if (limitLength > 0) {
int l = s.length();
if (l > limitLength) s.delete(limitLength, l);
else while (limitLength-- > l) s.append(prefixCharacter);
}
return s.reverse().toString();
}
/**
* 判断是否为数字或者大小写字母
*
* @param d 要判断的字符串
*/
private static void numericUpperAndLowerCaseLetterDetermination(String d) {
RuntimeException ex = new RuntimeException("相加的字符串内容只能为数字或者大小写字母:" + d);
if (d == null) throw ex;
for (char c : d.toCharArray()) {
if (c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || c > 'z') {
throw ex;
}
}
}
/**
* 获取差值
*
* @param c 要获取差值的字符
* @return 返回差值
*/
private static int characterDifference(char c) {
if (Character.isDigit(c)) {
return c - '0';
} else if (Character.isUpperCase(c)) {
return c - 'A' + 10;
} else {
return c - 'a' + 36;
}
}
/**
* 又差值还原到字符
*
* @param d 差值
* @return 还原后的字符
*/
private static char characterRestore(int d) {
int i = d % 62;
if (i < 10) {
return (char) (i + '0');
} else if (i < 36) {
return (char) (i + 'A' - 10);
} else {
return (char) (i + 'a' - 36);
}
}
/**
* code自增
* 使用的字符序【0~9A~Za~z】共62个字符,3个字符可计数62*62*62位
*
* @param code 要自增的code
* @param limitLength 长度限制
* @return 返回自增后的code
*/
public static String codeIncrement(String code, int limitLength) {
if (code == null) code = "";
return keepSpecifiedLength(characterAddition(code, "1"), '0', limitLength);
}
/**
* 字符自增
*
* @param code 要自增的字符
* @return 返回自增后的字符
*/
private static String codeIncrement(String code) {
return codeIncrement(code, -1);
}
/**
* 获取编码,并加lastCode自增
*
* @param parentCode 父级code
* @param lastCode 子级最后一个code
* @return 返回一个子级自增后的编码
*/
public static String getCode(String parentCode, String lastCode) {
return getCode(parentCode, lastCode, -1);
}
/**
* 获取编码,并加lastCode自增
*
* @param parentCode 父级code
* @param lastCode 子级最后一个code
* @param limitLength lastCode自增后的字符串长度
* @return 返回一个父级code+子级自增后的code字符串
*/
public static String getCode(String parentCode, String lastCode, int limitLength) {
if (lastCode == null) lastCode = "";
if (parentCode == null) {
return codeIncrement(lastCode, limitLength);
} else {
return parentCode + codeIncrement(lastCode.replaceFirst(parentCode, lastCode), limitLength);
}
}
public static void main(String[] args) {
String digit = "0";
String upperCase = "A";
String lowerCase = "a";
String code = "0";
for (int i = 0; i < 1000000; i++) {
System.out.println("数字自增【" + digit + "】\t,大写字母自增【" + upperCase + "】\t,小写字母自增【" + lowerCase + "】\t,组合自增【" + code + "】\t");
digit = digitalIncrement(digit);
upperCase = upperCaseIncrement(upperCase);
lowerCase = lowerCaseIncrement(lowerCase);
code = codeIncrement(code);
}
System.out.println(characterAddition("123", "123"));
System.out.println(digitAddition("9999", "9999"));
System.out.println(upperCaseAddition("ZZZ", "AZZ"));
System.out.println(upperCaseAddition("A", "B"));
System.out.println(lowerCaseAddition("zaz", "aza"));
System.out.println(digitalIncrement("999", 5));
System.out.println(digitalIncrement("999", 1));
System.out.println(digitalIncrement("999", 0));
System.out.println(digitalIncrement("999"));
System.out.println(upperCaseIncrement("ZZZ", 5));
System.out.println(upperCaseIncrement("ZZZ", 1));
System.out.println(upperCaseIncrement("ZZZ", 0));
System.out.println(upperCaseIncrement("ZZZ"));
System.out.println(lowerCaseIncrement("zzz", 5));
System.out.println(lowerCaseIncrement("zzz", 1));
System.out.println(lowerCaseIncrement("zzz", 0));
System.out.println(lowerCaseIncrement("zzz"));
System.out.println(getCode(null, "zzz", 10));
System.out.println(getCode(null, "zzz"));
System.out.println(codeIncrement("zzz"));
System.out.println(codeIncrement("9"));
System.out.println(codeIncrement("Z"));
System.out.println(codeIncrement("z"));
}
}