本文目录导读:

我来详细介绍Java中实现自动补全功能的几种常见方案:
Trie树(前缀树)实现
这是最经典的自动补全实现方式:
import java.util.*;
class TrieNode {
Map<Character, TrieNode> children = new HashMap<>();
boolean isEnd = false;
// 存储以该节点为结尾的词(可选)
String word;
}
public class AutoComplete {
private TrieNode root;
public AutoComplete() {
root = new TrieNode();
}
// 插入单词
public void insert(String word) {
TrieNode node = root;
for (char c : word.toCharArray()) {
node.children.putIfAbsent(c, new TrieNode());
node = node.children.get(c);
}
node.isEnd = true;
node.word = word;
}
// 搜索前缀
public List<String> search(String prefix) {
List<String> results = new ArrayList<>();
TrieNode node = root;
// 先找到前缀对应的节点
for (char c : prefix.toCharArray()) {
if (!node.children.containsKey(c)) {
return results;
}
node = node.children.get(c);
}
// DFS收集所有可能的词
dfs(node, results);
return results;
}
private void dfs(TrieNode node, List<String> results) {
if (node.isEnd) {
results.add(node.word);
}
for (TrieNode child : node.children.values()) {
dfs(child, results);
}
}
public static void main(String[] args) {
AutoComplete ac = new AutoComplete();
ac.insert("apple");
ac.insert("application");
ac.insert("appetite");
ac.insert("banana");
ac.insert("ball");
System.out.println("输入 'ap': " + ac.search("ap"));
System.out.println("输入 'ba': " + ac.search("ba"));
}
}
加权自动补全(带频率排序)
import java.util.*;
class WeightedAutoComplete {
static class WordFrequency {
String word;
int frequency;
WordFrequency(String word, int frequency) {
this.word = word;
this.frequency = frequency;
}
}
private Map<String, Integer> wordFreq = new HashMap<>();
private TrieNode root;
public WeightedAutoComplete() {
root = new TrieNode();
}
// 插入单词并设置频率
public void insert(String word, int frequency) {
wordFreq.put(word, frequency);
TrieNode node = root;
for (char c : word.toCharArray()) {
node.children.putIfAbsent(c, new TrieNode());
node = node.children.get(c);
}
node.isEnd = true;
}
// 按频率排序返回结果
public List<String> search(String prefix, int topK) {
List<String> results = search(prefix);
// 按频率排序
results.sort((a, b) ->
Integer.compare(
wordFreq.getOrDefault(b, 0),
wordFreq.getOrDefault(a, 0)
)
);
// 返回前K个
return results.subList(0, Math.min(topK, results.size()));
}
// 基本搜索方法(同上)
private List<String> search(String prefix) {
List<String> results = new ArrayList<>();
TrieNode node = root;
for (char c : prefix.toCharArray()) {
if (!node.children.containsKey(c)) {
return results;
}
node = node.children.get(c);
}
dfs(node, prefix, results);
return results;
}
private void dfs(TrieNode node, String prefix, List<String> results) {
if (node.isEnd) {
results.add(prefix);
}
for (Map.Entry<Character, TrieNode> entry : node.children.entrySet()) {
dfs(entry.getValue(), prefix + entry.getKey(), results);
}
}
}
使用Lucene实现(企业级方案)
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
public class LuceneAutoComplete {
private Directory directory;
private StandardAnalyzer analyzer;
public LuceneAutoComplete() {
directory = new RAMDirectory();
analyzer = new StandardAnalyzer();
}
public void buildIndex(List<String> words) throws Exception {
IndexWriterConfig config = new IndexWriterConfig(analyzer);
IndexWriter writer = new IndexWriter(directory, config);
for (String word : words) {
Document doc = new Document();
doc.add(new TextField("word", word, Field.Store.YES));
writer.addDocument(doc);
}
writer.close();
}
public List<String> autoComplete(String prefix) throws Exception {
List<String> results = new ArrayList<>();
IndexReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
// 使用前缀查询
Query query = new PrefixQuery(new Term("word", prefix));
TopDocs topDocs = searcher.search(query, 10);
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
Document doc = searcher.doc(scoreDoc.doc);
results.add(doc.get("word"));
}
reader.close();
return results;
}
}
基于N-gram的模糊自动补全
import java.util.*;
import java.util.stream.*;
public class NGramAutoComplete {
private Map<String, Set<String>> ngramIndex = new HashMap<>();
private int ngramSize = 2;
public NGramAutoComplete(int ngramSize) {
this.ngramSize = ngramSize;
}
// 生成n-gram
private List<String> generateNGrams(String word) {
List<String> ngrams = new ArrayList<>();
String paddedWord = "#" + word + "#"; // 添加边界标记
for (int i = 0; i <= paddedWord.length() - ngramSize; i++) {
ngrams.add(paddedWord.substring(i, i + ngramSize));
}
return ngrams;
}
// 添加单词到索引
public void addWord(String word) {
List<String> ngrams = generateNGrams(word);
for (String ngram : ngrams) {
ngramIndex.computeIfAbsent(ngram, k -> new HashSet<>())
.add(word);
}
}
// 搜索
public List<String> search(String query) {
List<String> queryNGrams = generateNGrams(query);
// 找到包含所有n-gram的单词
Set<String> result = new HashSet<>();
boolean first = true;
for (String ngram : queryNGrams) {
if (ngramIndex.containsKey(ngram)) {
if (first) {
result.addAll(ngramIndex.get(ngram));
first = false;
} else {
result.retainAll(ngramIndex.get(ngram));
}
} else {
return Collections.emptyList();
}
}
// 按匹配度排序
return result.stream()
.sorted(Comparator.comparingInt(word ->
-levenshteinDistance(word, query)))
.collect(Collectors.toList());
}
// 编辑距离计算
private int levenshteinDistance(String s1, String s2) {
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
for (int i = 0; i <= s1.length(); i++) dp[i][0] = i;
for (int j = 0; j <= s2.length(); j++) dp[0][j] = j;
for (int i = 1; i <= s1.length(); i++) {
for (int j = 1; j <= s2.length(); j++) {
int cost = s1.charAt(i-1) == s2.charAt(j-1) ? 0 : 1;
dp[i][j] = Math.min(Math.min(
dp[i-1][j] + 1,
dp[i][j-1] + 1),
dp[i-1][j-1] + cost
);
}
}
return dp[s1.length()][s2.length()];
}
}
使用示例
public class AutoCompleteDemo {
public static void main(String[] args) {
// 使用Trie树
AutoComplete trieComplete = new AutoComplete();
trieComplete.insert("java");
trieComplete.insert("javascript");
trieComplete.insert("python");
trieComplete.insert("javaee");
System.out.println("Trie自动补全 'ja': " + trieComplete.search("ja"));
// 使用加权自动补全
WeightedAutoComplete weightedComplete = new WeightedAutoComplete();
weightedComplete.insert("java", 100);
weightedComplete.insert("javascript", 50);
weightedComplete.insert("javaee", 30);
System.out.println("加权自动补全 'ja': " + weightedComplete.search("ja", 5));
}
}
选择建议
- Trie树:适合小规模数据,内存占用小,速度快
- 加权自动补全:需要排序结果时使用
- Lucene:适合大规模数据,企业级应用
- N-gram:支持模糊匹配和拼写纠错
根据实际需求选择合适方案,考虑因素包括数据量、响应速度、内存限制等。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播