欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java實(shí)現(xiàn)中文模糊查詢的示例代碼

 更新時(shí)間:2025年06月10日 09:32:54   作者:Katie。  
Java作為后端主流語言,承擔(dān)著絕大多數(shù)企業(yè)級應(yīng)用的檢索功能,如何在Java中實(shí)現(xiàn)中文模糊查詢,兼顧準(zhǔn)確率和性能,是企業(yè)和開發(fā)者面對的共同挑戰(zhàn),下面我們就來看看具體實(shí)現(xiàn)方法吧

1. 項(xiàng)目背景詳細(xì)介紹

1.1 檢索體驗(yàn)現(xiàn)狀

隨著互聯(lián)網(wǎng)和移動應(yīng)用的普及,用戶越來越習(xí)慣于“所見即所得”的搜索體驗(yàn)。傳統(tǒng)的精確匹配(Exact Match)已經(jīng)無法滿足用戶在海量中文數(shù)據(jù)中進(jìn)行快速定位的需求——拼寫錯(cuò)誤、輸入法候選詞偏差、用戶記憶模糊等都會導(dǎo)致精確匹配失敗。

1.2 模糊查詢的重要性

模糊查詢(Fuzzy Search)通過對關(guān)鍵詞進(jìn)行相似度或近似度計(jì)算,能夠容忍用戶輸入的錯(cuò)別字、音近字、簡繁體差異等。它在電商商品搜索、企業(yè)通訊錄檢索、日志分析、智能客服、醫(yī)療診斷輔助等場景中發(fā)揮著至關(guān)重要的作用。

1.3 Java 平臺的應(yīng)用場景

Java 作為后端主流語言,承擔(dān)著絕大多數(shù)企業(yè)級應(yīng)用的檢索功能。如何在 Java 中高效、可擴(kuò)展地實(shí)現(xiàn)中文模糊查詢,兼顧準(zhǔn)確率和性能,是企業(yè)和開發(fā)者面對的共同挑戰(zhàn)。

2. 項(xiàng)目需求詳細(xì)介紹

2.1 功能性需求

  • 支持拼寫糾錯(cuò):對用戶輸入的錯(cuò)別字進(jìn)行糾正,如“北京”可匹配“北京”;
  • 支持拼音首字母和全拼匹配:如“bj”或“beijing”均可匹配“北京”;
  • 支持簡繁體互轉(zhuǎn):輸入“國家”也可匹配“國家”;
  • 支持編輯距離匹配:允許1–2個(gè)字符的插入、刪除、替換;
  • 基于數(shù)據(jù)庫與內(nèi)存雙模式:既可對 MySQL/Oracle 等數(shù)據(jù)庫的指定字段進(jìn)行 LIKE+補(bǔ)償算法查詢,也可對內(nèi)存中 Java 對象列表進(jìn)行快速檢索;
  • 提供分頁排序:允許按照匹配度或相關(guān)度排序,并支持分頁加載;
  • 簡單易用 API:封裝成 Java 類庫,支持 Maven/Gradle 一鍵引入;

2.2 非功能性需求

高性能:100 萬級記錄內(nèi)存檢索毫秒級返回;數(shù)據(jù)庫檢索在索引列上 100ms 內(nèi)響應(yīng);

易擴(kuò)展:可插拔分詞器(IKAnalyzer、HanLP 等)、可替換相似度算法(Jaro-Winkler、Cosine、TF-IDF+BM25);

可維護(hù)性:模塊化設(shè)計(jì)、單元測試覆蓋率≥90%,可生成 JavaDoc 文檔;

兼容性:Java 8+;數(shù)據(jù)庫可兼容主流 RDBMS;

3. 相關(guān)技術(shù)詳細(xì)介紹

3.1 中文分詞與拼音處理

IKAnalyzer:基于 Lucene 的輕量級中文分詞器,效率高、精度好;

HanLP:功能完備,支持命名實(shí)體識別等高級 NLP 功能;

pinyin4j:用于中文轉(zhuǎn)拼音、獲取聲母、韻母;

3.2 相似度與編輯距離算法

Levenshtein 編輯距離:衡量兩個(gè)字符串之間的最小編輯操作數(shù);

Damerau–Levenshtein:在編輯距離基礎(chǔ)上加入相鄰字符交換;

Jaro–Winkler:對短字符串(人名、地名)效果更好;

3.3 數(shù)據(jù)庫 LIKE 優(yōu)化

前綴匹配索引:WHERE col LIKE '北京%' 可走索引;

倒排索引模擬:將字段拆分為 n-gram 存儲,并對 n-gram 建索引;

全文索引:MySQL InnoDB 支持全文檢索,但對中文支持有限;

4. 實(shí)現(xiàn)思路詳細(xì)介紹

4.1 系統(tǒng)架構(gòu)

core 模塊:提供 FuzzySearchService 接口及默認(rèn)實(shí)現(xiàn)

pinyin 模塊:封裝 PinyinConverter,提供全拼、首字母轉(zhuǎn)換等

distance 模塊:封裝多種相似度計(jì)算器,如 LevenshteinDistance、JaroWinklerDistance

db 模塊:DatabaseSearchService,對接 JDBC,實(shí)現(xiàn)基于 LIKE+補(bǔ)償算法的模糊查詢

memory 模塊:InMemorySearchService,對 Java 對象列表進(jìn)行索引與檢索

4.2 數(shù)據(jù)處理流程

標(biāo)準(zhǔn)化:輸入關(guān)鍵詞去除空白、統(tǒng)一簡繁體、轉(zhuǎn)為小寫;

拼音擴(kuò)展:生成全拼、首字母兩個(gè)維度的候選關(guān)鍵詞;

分詞:對數(shù)據(jù)庫字段或內(nèi)存對象屬性進(jìn)行分詞,生成 n-gram 或詞元列表;

匹配:

  • 內(nèi)存模式:對每個(gè)對象屬性字符串計(jì)算相似度評分,過濾閾值以上結(jié)果;
  • 數(shù)據(jù)庫模式:先用 LIKE '%key%' 或 n-gram 索引粗篩,再在 Java 端補(bǔ)償計(jì)算真實(shí)相似度;
  • 排序與分頁:根據(jù)相似度打分降序排序,截取指定頁碼結(jié)果;

5. 完整實(shí)現(xiàn)代碼

// 文件:core/FuzzySearchService.java
package com.example.fuzzy.core;
import java.util.List;
import java.util.Map;
/**
 * 模糊查詢服務(wù)接口
 */
public interface FuzzySearchService<T> {
    /**
     * 對內(nèi)存數(shù)據(jù)列表進(jìn)行模糊查詢
     * @param dataList 待檢索對象列表
     * @param fieldExtractor 字段提取器,返回待匹配字符串
     * @param keyword 用戶輸入關(guān)鍵詞
     * @param topK 返回前 K 名排序結(jié)果
     * @return 匹配結(jié)果列表
     */
    List<T> searchInMemory(List<T> dataList,
                           FieldExtractor<T> fieldExtractor,
                           String keyword,
                           int topK);
 
    /**
     * 對數(shù)據(jù)庫指定表字段進(jìn)行模糊查詢
     * @param tableName 表名
     * @param columnName 列名
     * @param keyword 用戶輸入關(guān)鍵詞
     * @param params JDBC 參數(shù)(如分頁)
     * @return 查詢結(jié)果列表,每條記錄為列名→值的 Map
     */
    List<Map<String, Object>> searchInDatabase(String tableName,
                                               String columnName,
                                               String keyword,
                                               Map<String, Object> params);
}
 
// 文件:core/FieldExtractor.java
package com.example.fuzzy.core;
/**
 * 字段提取器,用于從對象中獲取待匹配字符串
 */
public interface FieldExtractor<T> {
    String extract(T obj);
}
 
// 文件:pinyin/PinyinConverter.java
package com.example.fuzzy.pinyin;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.*;
/**
 * 拼音轉(zhuǎn)換工具
 */
public class PinyinConverter {
    private static HanyuPinyinOutputFormat fmt = new HanyuPinyinOutputFormat();
    static {
        fmt.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        fmt.setVCharType(HanyuPinyinVCharType.WITH_V);
    }
    /** 獲取字符串全拼,如“北京”→“beijing” */
    public static String toPinyin(String chinese) throws BadHanyuPinyinOutputFormatCombination {
        StringBuilder sb = new StringBuilder();
        for (char c : chinese.toCharArray()) {
            if (Character.toString(c).matches("[\\u4E00-\\u9FA5]+")) {
                String[] arr = PinyinHelper.toHanyuPinyinStringArray(c, fmt);
                sb.append(arr[0]);
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
    /** 獲取拼音首字母,如“北京”→“bj” */
    public static String toPinyinInitials(String chinese) throws BadHanyuPinyinOutputFormatCombination {
        StringBuilder sb = new StringBuilder();
        for (char c : chinese.toCharArray()) {
            if (Character.toString(c).matches("[\\u4E00-\\u9FA5]+")) {
                String[] arr = PinyinHelper.toHanyuPinyinStringArray(c, fmt);
                sb.append(arr[0].charAt(0));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}
 
// 文件:distance/LevenshteinDistance.java
package com.example.fuzzy.distance;
/**
 * 編輯距離算法實(shí)現(xiàn)
 */
public class LevenshteinDistance {
    public static int compute(String s1, String s2) {
        int n = s1.length(), m = s2.length();
        int[][] dp = new int[n+1][m+1];
        for (int i = 0; i <= n; i++) dp[i][0] = i;
        for (int j = 0; j <= m; j++) dp[0][j] = j;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; 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[n][m];
    }
    /** 歸一化相似度 = 1 - distance/maxLen */
    public static double similarity(String s1, String s2) {
        int dist = compute(s1, s2);
        int max = Math.max(s1.length(), s2.length());
        return max == 0 ? 1.0 : 1.0 - (double) dist / max;
    }
}
 
// 文件:core/impl/InMemorySearchServiceImpl.java
package com.example.fuzzy.core.impl;
import com.example.fuzzy.core.*;
import com.example.fuzzy.distance.LevenshteinDistance;
import com.example.fuzzy.pinyin.PinyinConverter;
import java.util.*;
/**
 * 內(nèi)存模糊查詢實(shí)現(xiàn)
 */
public class InMemorySearchServiceImpl<T> implements FuzzySearchService<T> {
    @Override
    public List<T> searchInMemory(List<T> dataList,
                                  FieldExtractor<T> fieldExtractor,
                                  String keyword,
                                  int topK) {
        List<Result<T>> results = new ArrayList<>();
        // 預(yù)處理關(guān)鍵詞
        String kw = preprocess(keyword);
        String kwPinyin = toPinyinSafe(kw);
        String kwInitials = toInitialsSafe(kw);
        for (T item : dataList) {
            String text = fieldExtractor.extract(item);
            String txt = preprocess(text);
            // 原文相似度
            double simText = LevenshteinDistance.similarity(txt, kw);
            // 拼音相似度
            double simPin = LevenshteinDistance.similarity(toPinyinSafe(txt), kwPinyin);
            // 首字母相似度
            double simInit = LevenshteinDistance.similarity(toInitialsSafe(txt), kwInitials);
            double score = Math.max(Math.max(simText, simPin), simInit);
            if (score > 0.5) {
                results.add(new Result<>(item, score));
            }
        }
        // 排序并截取 topK
        results.sort((a, b) -> Double.compare(b.score, a.score));
        List<T> top = new ArrayList<>();
        for (int i = 0; i < Math.min(topK, results.size()); i++) {
            top.add(results.get(i).data);
        }
        return top;
    }
    private String preprocess(String s) {
        return s == null ? "" : s.trim().toLowerCase();
    }
    private String toPinyinSafe(String s) {
        try { return PinyinConverter.toPinyin(s); }
        catch (Exception e) { return s; }
    }
    private String toInitialsSafe(String s) {
        try { return PinyinConverter.toPinyinInitials(s); }
        catch (Exception e) { return s; }
    }
 
    @Override
    public List<Map<String, Object>> searchInDatabase(String tableName, String columnName, String keyword, Map<String, Object> params) {
        // 簡化示例:只演示 SQL 構(gòu)造
        String sql = "SELECT * FROM " + tableName +
                     " WHERE " + columnName + " LIKE ? " +
                     "ORDER BY LENGTH(" + columnName + ") ASC " +
                     "LIMIT ?, ?";
        // 參數(shù):%keyword%, offset, pageSize
        // JDBC 執(zhí)行略
        return Collections.emptyList();
    }
}
 
// 輔助類
class Result<T> {
    T data;
    double score;
    Result(T data, double score) { this.data = data; this.score = score; }
}

6. 代碼詳細(xì)解讀

FuzzySearchService 接口:定義內(nèi)存和數(shù)據(jù)庫兩種模糊查詢方法,統(tǒng)一調(diào)用入口;

FieldExtractor 接口:用于提取對象中待匹配的文本字段,實(shí)現(xiàn)與業(yè)務(wù)對象解耦;

PinyinConverter:基于 pinyin4j 將中文轉(zhuǎn)換為全拼和首字母,輔助拼音匹配;

LevenshteinDistance:經(jīng)典編輯距離算法及歸一化相似度計(jì)算,用于度量字符串相似度;

InMemorySearchServiceImpl:

  • 預(yù)處理:去空格、轉(zhuǎn)換小寫、簡繁體可擴(kuò)展;
  • 多維度匹配:原文、全拼、首字母三種相似度計(jì)算,取最大值作為最終得分;
  • 閾值過濾:只保留相似度 >0.5 的候選結(jié)果;
  • 排序與分頁:按得分降序并截取前 K;

Database 模式(示例):

  • 構(gòu)造基于 LIKE '%keyword%' 的 SQL 粗篩;
  • 可結(jié)合 n-gram 索引與 Java 端補(bǔ)償算法提升準(zhǔn)確度;

7. 項(xiàng)目詳細(xì)總結(jié)

本項(xiàng)目以純 Java 實(shí)現(xiàn)了對中文數(shù)據(jù)的模糊查詢,支持編輯距離、拼音全拼與首字母匹配,既可對內(nèi)存列表進(jìn)行高效檢索,也可與關(guān)系型數(shù)據(jù)庫結(jié)合使用。模塊化設(shè)計(jì)易于擴(kuò)展新分詞器、相似度算法和繁體簡體轉(zhuǎn)換策略。

8. 項(xiàng)目常見問題及解答

Q1:為什么要同時(shí)使用原文、拼音和首字母匹配?

A1:中文用戶輸入習(xí)慣多樣,有時(shí)輸入漢字、有時(shí)輸入拼音,或只輸入首字母拼寫縮寫,多維度匹配可覆蓋更多場景。

Q2:編輯距離算法性能如何優(yōu)化?

A2:可采用 Ukkonen 提前剪枝、基于 Trie 的多模式匹配,或?qū)狳c(diǎn)查詢轉(zhuǎn)為規(guī)則正則,加速過濾。

Q3:數(shù)據(jù)庫 LIKE 查詢?yōu)楹螣o法完全滿足需求?

A3:LIKE 無法處理錯(cuò)別字與拼音匹配;同時(shí)大數(shù)據(jù)量時(shí) %keyword% 會導(dǎo)致全表掃描。

9. 擴(kuò)展方向與性能優(yōu)化

分布式檢索:使用 Elasticsearch/Solr 等引擎替代關(guān)系型數(shù)據(jù)庫,利用倒排索引與分詞插件;

多線程并行:內(nèi)存模式下對大規(guī)模列表采用 Fork/Join 或并行流;

專用字典:集成行業(yè)領(lǐng)域同義詞、專有名詞詞典,提升匹配準(zhǔn)確率;

動態(tài)閾值:結(jié)合機(jī)器學(xué)習(xí)模型,根據(jù)用戶行為動態(tài)調(diào)整相似度閾值和排序權(quán)重;

緩存與預(yù)熱:對熱點(diǎn)關(guān)鍵詞結(jié)果做緩存,降低重復(fù)計(jì)算開銷。

以上就是java實(shí)現(xiàn)中文模糊查詢的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于java模糊查詢的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 如何在java中使用SFTP協(xié)議安全的傳輸文件

    如何在java中使用SFTP協(xié)議安全的傳輸文件

    這篇文章主要介紹了如何在java中使用SFTP協(xié)議安全的傳輸文件,幫助大家更好的理解和使用JSch,感興趣的朋友可以了解下
    2020-10-10
  • 親手帶你解決Debug Fastjson的安全漏洞

    親手帶你解決Debug Fastjson的安全漏洞

    這篇文章主要介紹了親手帶你解決Debug Fastjson的安全漏洞,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 詳解Android開發(fā)中Fragment的使用

    詳解Android開發(fā)中Fragment的使用

    這篇文章主要介紹了詳解Android開發(fā)中Fragment的使用,包括Java代碼中調(diào)用Fragment的方法,需要的朋友可以參考下
    2015-07-07
  • MyBatisX逆向工程的實(shí)現(xiàn)示例

    MyBatisX逆向工程的實(shí)現(xiàn)示例

    本文主要介紹了MyBatisX逆向工程的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-05-05
  • Java實(shí)現(xiàn)字符串反轉(zhuǎn)的常用方法小結(jié)

    Java實(shí)現(xiàn)字符串反轉(zhuǎn)的常用方法小結(jié)

    在Java中,你可以使用多種方法來反轉(zhuǎn)字符串,這篇文章主要為大家整理了幾種常見的反轉(zhuǎn)字符串的方法,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • 一文帶你真正理解Java中的內(nèi)部類

    一文帶你真正理解Java中的內(nèi)部類

    不知道大家在平時(shí)的開發(fā)過程中或者源碼里是否留意過內(nèi)部類,那有思考過為什么要有內(nèi)部類,內(nèi)部類都有哪幾種形式,本篇文章主要帶領(lǐng)大家理解下這塊內(nèi)容
    2022-08-08
  • springboot在idea下debug調(diào)試熱部署問題

    springboot在idea下debug調(diào)試熱部署問題

    這篇文章主要介紹了springboot在idea下debug調(diào)試熱部署問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Spring?RestTemplate遠(yuǎn)程調(diào)用過程

    Spring?RestTemplate遠(yuǎn)程調(diào)用過程

    這篇文章主要介紹了Spring?RestTemplate遠(yuǎn)程調(diào)用過程,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java獲取當(dāng)前時(shí)間方法總結(jié)

    Java獲取當(dāng)前時(shí)間方法總結(jié)

    本篇文章給大家整理了關(guān)于Java獲取當(dāng)前時(shí)間方法,以及相關(guān)代碼分享,有需要的朋友測試參考下吧。
    2018-02-02
  • Spring?Validation參數(shù)效驗(yàn)的各種使用姿勢總結(jié)

    Spring?Validation參數(shù)效驗(yàn)的各種使用姿勢總結(jié)

    在實(shí)際項(xiàng)目中經(jīng)常需要對前段傳來的數(shù)據(jù)進(jìn)行校驗(yàn),下面這篇文章主要給大家介紹了關(guān)于Spring?Validation參數(shù)效驗(yàn)的各種使用姿勢,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04

最新評論