Java大词库押韵搜索性能优化方案咨询
嘿,我来帮你把押韵搜索的速度提上去!你的场景是Angular实时提示,延迟必须压到10秒以内,当前1分钟的耗时确实太影响体验了。先拆解下现有代码的问题,再给你几个实操性拉满的优化方案:
一、先填现有代码的坑
- 线程安全隐患:你用的
HashSet<String> rhymesList不是线程安全的,多线程同时add会导致数据丢失或者并发修改异常,得换成CopyOnWriteArraySet,或者用Collections.synchronizedSet包装一下。 - 正则表达式的低效:用
matches(regex)做后缀匹配完全是大材小用!押韵搜索本质是后缀匹配,直接用String.endsWith(word)比正则快N倍,还能避免每次请求都编译正则的开销。 - 线程池滥用:给每个词都创建一个Runnable任务,1000万词就有1000万个任务,线程切换的开销会把性能拖垮。不如把字典分成几个大块,每个块交给一个线程处理,大幅减少任务数量。
二、核心优化:用反向索引实现O(log n)级查询
这是能让你直接把查询压到毫秒级的关键方案:
- 思路:把每个词反转后存入有序集合(比如
TreeSet),搜索时把目标词也反转,然后找所有以反转后目标词为前缀的元素——这些元素反转回来,就是原词中以目标词结尾的押韵词。 - 为什么高效?
TreeSet基于红黑树实现,前缀搜索可以用subSet方法快速定位范围,时间复杂度是O(log n),遍历匹配结果的开销也极小。
三、优化后的完整代码示例
package pl.kamilkoszykowski.dopewriter; import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; @RestController @CrossOrigin("http://localhost:4200") public class Controller { // 预加载反转后的词库,用TreeSet实现快速前缀搜索 private final TreeSet<String> reversedDictionary = loadReversedDictionary(); @GetMapping("/rhyme/{word}") public Set<String> rhymes(@PathVariable String word) { // 反转搜索词,用于前缀匹配 String reversedWord = new StringBuilder(word).reverse().toString(); // 快速定位所有以反转词为前缀的元素 SortedSet<String> matchedReversed = reversedDictionary.subSet( reversedWord, reversedWord + Character.MAX_VALUE ); // 反转回来得到最终押韵词集合 Set<String> rhymesList = new HashSet<>(); for (String reversed : matchedReversed) { rhymesList.add(new StringBuilder(reversed).reverse().toString()); } return rhymesList; } private TreeSet<String> loadReversedDictionary() { TreeSet<String> reversedSet = new TreeSet<>(); try { String content = Files.readString(Paths.get("src/main/resources/dictionary.txt")); String[] words = content.split(","); for (String word : words) { String trimmedWord = word.trim(); if (!trimmedWord.isEmpty()) { reversedSet.add(new StringBuilder(trimmedWord).reverse().toString()); } } } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("Failed to load dictionary file"); } return reversedSet; } }
四、额外优化建议
- 字典预处理:加载时自动去掉空字符串和重复词,避免无效数据占用内存、拖慢查询。
- 大小写统一:如果词库有大小写混合,加载时统一转成小写(或大写),查询时也同步转换,避免漏匹配。
- 前缀树替代TreeSet:如果TreeSet内存占用过高,可以换成
Trie(前缀树)结构,专门用于前缀搜索,内存占用更优,查询速度同样出色。 - 前端防抖:Angular实时提示要加防抖(比如300ms),避免用户每输入一个字符就发请求,减少后端压力。
五、为什么不用MySQL?
你说得太对了!MySQL的LIKE '%rhyme'会触发全表扫描,性能比内存查询差几个量级,而且实时提示场景下,数据库的网络IO和查询延迟完全没法满足要求,内存查询才是正确选择。
这个方案应该能轻松把你的查询时间压到1秒以内,完全适配实时提示的需求!
内容的提问来源于stack exchange,提问作者Ugabugaman
相关产品推荐
相关产品推荐

