字符串进行前缀匹配
问题
工作中接到这样一个需求
货品编号的前缀是由分类的编号组成的, 用户输入货品编号的时候自动回显出货品的分类.
而 货品的分类编号是不规则的
如: 0401,020508,27800101
每一个编号的长度都是不同的, 不过好在是相同数组开头的编号长度都是统一的.
所以不免有些人会冒出写死匹配的想法. 但是这也太low了.
经过不懈的尝试和努力终于找到了两种解决方案.
第一种
得到货品编号的时候, 以步长为1 从后向前依次截取字符串, 将截取后的字符串去分类编号中进行匹配,如匹配到了则将分类信息返回, 如匹配不到则继续进行匹配,直到匹配到为止;
代码示例
public GoodsCategoryEntity getByGoodsIdnumber(String goodsIdnumber) {
// 获取所有的分类信息
List<GoodsCategoryEntity> list = this.list();
// 以编码为key, 转换成map集合
Map<String, GoodsCategoryEntity> goodsCategoryMap = list.stream().collect(Collectors.toMap(GoodsCategoryEntity::getIdnumber, Function.identity()));
// 调用编码截取匹配方法
GoodsCategoryEntity goodsGategory = getGoodsGategory(goodsIdnumber, goodsCategoryMap);
return goodsGategory;
}
//从货品编码中匹配出货品分类编码
private GoodsCategoryEntity getGoodsGategory(String goodsIdnumber, Map<String, GoodsCategoryEntity> goodsCategoryMap) {
if (Func.isBlank(goodsIdnumber)) {
return new GoodsCategoryEntity();
}
// 进行字符串截取
String substring = goodsIdnumber.substring(0, goodsIdnumber.length() - 1);
// 如果截取出的编码匹配到了分类信息则返回, 没匹配到则进行递归继续匹配
return Func.isEmpty(goodsCategoryMap.get(substring))?getGoodsGategory(substring, goodsCategoryMap):goodsCategoryMap.get(substring);
}
第二种
其实思想同上述方法差不多,只不过上述方法是从后往前匹配, 该方法是从前往后匹配;
从前往后就要比从后往前多考虑一些, 因为分类编码存在父子级关系
比如: 04 和 0401 和 040101
所以不能匹配到一个就结束.
上代码
# 首先要创建一个前缀树
public class Trie implements Serializable {
private static final long serialVersionUID = 1L;
public static class Node{
public Map<Character, Node> children = new HashMap<>();
}
public Node root = new Node();
public Trie() {
}
// 将元素填入前缀字典中
public void insert(String word) {
Node pre = this.root;
for(char c: word.toCharArray()){
Node node = pre.children.get(c);
if(node == null){
node = new Node();
pre.children.put(c, node);
}
pre = node;
}
}
// 查询前缀并返回前缀
public String search(String word) {
Node pre = this.root;
String str = "";
for(char c: word.toCharArray()){
Node node = pre.children.get(c);
if(node == null) return str;
str = str + c;
pre = node;
}
return str;
}
}
public GoodsCategoryEntity getByGoodsIdnumber(String goodsIdnumber) {
// 获取所有的分类信息
List<GoodsCategoryEntity> list = this.list(Wrappers.lambdaQuery(GoodsCategoryEntity.class).select(GoodsCategoryEntity::getIdnumber));
// 填充好前赘树
Trie trie = new Trie();
for (GoodsCategoryEntity category : list) {
trie_map.insert(string);
}
# 以上步骤可以提前预制好, 并将trie对象序列化存在缓存中, 在使用时直接从缓存中获取 trie 对象
// 进行编码匹配
String number = trie_map.search(goodsIdnumber);
// 通过编码获取分类对象
GoodsCategoryEntity entity = this.getOne(Wrappers.lambdaQuery(GoodsCategoryEntity.class).eq(GoodsCategoryEntity::getIdnumber,number ));
return Func.isEmpty(entity )?new GoodsCategoryEntity ():entity;
}