Skip to content

IKAnalyzer 分词器

需求:使用 Lucene 和 IKAnalyzer 从文章中提取频繁出现的关键字,并按出现次数倒序排列

引入依赖

xml
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>8.11.2</version>
</dependency>

<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-analyzers-common</artifactId>
    <version>8.11.2</version>
</dependency>

<!-- IKAnalyzer for Lucene 8.x -->
<dependency>
    <groupId>com.github.magese</groupId>
    <artifactId>ik-analyzer</artifactId>
    <version>8.5.0</version>
</dependency>

工具类代码

java
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.IOException;
import java.io.StringReader;
import java.util.*;

/**
 * 文本关键词提取工具类
 * <p>
 * 基于 Lucene 和 IKAnalyzer 实现,用于从文章中提取频繁出现的关键字,并按出现次数倒序排列
 * 该类内部含有一个List2Map方法,可将重复<String>集合转换为Map<String, Integer>格式
 * 并算出该<String>重复次数,放入相应的value中
 * </p>
 *
 * @author xxl
 * @date 2026/2/5 13:08
 */
public class LuceneUtil {

    /**
     * 测试文章内容
     */
    static String keyWord = "笑星潘长江当上\"男媒婆\"了,但这回可不是演小品——由他自编自导自演的都市喜剧《男媒婆》将于1月13日在北"
            + "京卫视首播。剧中,潘长江变身成为能说会道、古道热肠的\"新时代男媒婆\"丁二春,与\"台湾第一酒窝美女2\"张庭上演了一出\"屌丝逆袭\""
            + "的浪漫追爱记。李明启、李文启、冯远征、任程伟、马丽、徐等明星也齐齐上阵制造\"笑料\"《男媒婆》围绕着丁二春和他所开"
            + "办的\"全成热恋\"婚介所展开。人到中年的丁二春眼看来势汹涌的\"婚恋大潮\"商机不断,想凭借一张巧嘴开创事业和人生"
            + "\"第二春\"。婚介所开张大吉,顾客盈门,提出的要求却也千奇百怪,拜金女、宅男、小老板粉墨登场,展开了一系列令人捧腹"
            + "大笑又不失温情的精彩故事。剧中的一大看点是美女搭配\"丑男\"的搭配,张庭与潘长江成了一对欢喜冤家。张庭表示,剧中两人\""
            + "身高有差距、年龄有距离,相貌不对等\"。而潘长江谈到这种主角设定时认为:\"张庭以往的角色都特别独立、可爱,而'大女人'和'"
            + "小男人'正是我俩这对情侣的设定,所以张庭是非常合适的人选。\"此外,该剧也是潘长江继《能人冯天贵》、《清凌凌的水蓝莹莹的天》"
            + "第一、第二部之后第四次自导自演的喜剧作品。潘长江表示,全剧通过\"媒婆\"这个特殊职业的视角,展示着当代社会种种婚恋价值观,涵盖了"
            + "黄昏恋、拜金女、凤凰男等诸多引发热议的时代话题。(记者 尹春芳)免责声明:本文仅代表作者个人观点,与环球网无关。其原创性以及文中"
            + "陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,"
            + "并请自行核实相关内容。";

    /**
     * 默认返回关键字个数
     */
    private final static Integer NUM = 5;

    /**
     * 默认截取关键字的最小长度
     */
    private final static Integer QUANTITY = 1;

    /**
     * 从文章中提取关键词
     * <p>
     * 使用 IKAnalyzer 对文章进行分词,提取长度大于指定值的词
     * </p>
     *
     * @param article   待分析的文章内容
     * @param minLength 关键词最小长度
     * @return 提取的关键词列表
     * @throws IOException 分词过程中可能出现的IO异常
     */
    private static List<String> extract(String article, Integer minLength) throws IOException {
        List<String> keywordList = new ArrayList<>();

        // 使用 try-with-resources 自动管理资源
        try (IKAnalyzer analyzer = new IKAnalyzer(true);
             TokenStream tokenStream = analyzer.tokenStream("", new StringReader(article))) {

            // 重置 TokenStream
            tokenStream.reset();

            // 遍历分词结果
            while (tokenStream.incrementToken()) {
                CharTermAttribute charTermAttribute = tokenStream.getAttribute(CharTermAttribute.class);
                String keyword = charTermAttribute.toString();

                // 过滤长度小于指定值的词
                if (keyword.length() > minLength) {
                    keywordList.add(keyword);
                }
            }

            // 结束 TokenStream
            tokenStream.end();
        }

        return keywordList;
    }

    /**
     * 将关键词列表转换为词频映射
     * <p>
     * 统计每个关键词出现的次数
     * </p>
     *
     * @param keywordList 关键词列表
     * @return 关键词到出现次数的映射
     */
    private static Map<String, Integer> list2Map(List<String> keywordList) {
        Map<String, Integer> frequencyMap = new HashMap<>();

        // 统计每个关键词出现的次数
        for (String keyword : keywordList) {
            frequencyMap.put(keyword, frequencyMap.getOrDefault(keyword, 0) + 1);
        }

        return frequencyMap;
    }

    /**
     * 提取文章关键词并排序
     * <p>
     * 从文章中提取关键词,按出现次数倒序排列,返回指定数量的关键词
     * </p>
     *
     * @param article   待分析的文章内容
     * @param minLength 关键词最小长度
     * @param topN      返回的关键词数量
     * @return 按出现次数倒序排列的关键词数组
     * @throws IOException 分词过程中可能出现的IO异常
     */
    public static String[] getKeyWords(String article, Integer minLength, Integer topN) throws IOException {
        // 提取关键词
        List<String> keywordList = extract(article, minLength);

        // 统计词频
        Map<String, Integer> frequencyMap = list2Map(keywordList);

        // 按词频倒序排序
        ArrayList<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(frequencyMap.entrySet());
        Collections.sort(sortedEntries, new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                return o2.getValue().compareTo(o1.getValue());
            }
        });

        // 确保返回数量不超过实际关键词数量
        if (sortedEntries.size() < topN) {
            topN = sortedEntries.size();
        }

        // 构建返回结果
        String[] result = new String[topN];
        for (int i = 0; i < topN; i++) {
            result[i] = sortedEntries.get(i).getKey();
        }

        return result;
    }

    /**
     * 提取文章关键词(使用默认参数)
     * <p>
     * 使用默认的关键词最小长度和返回数量
     * </p>
     *
     * @param article 待分析的文章内容
     * @return 按出现次数倒序排列的关键词数组
     * @throws IOException 分词过程中可能出现的IO异常
     */
    public static String[] getKeyWords(String article) throws IOException {
        return getKeyWords(article, QUANTITY, NUM);
    }

    /**
     * 主方法,用于测试关键词提取功能
     *
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        try {
            // 提取关键词
            String[] keywords = getKeyWords(keyWord);

            // 输出结果
            System.out.println("提取的关键词(按出现次数倒序):");
            for (int i = 0; i < keywords.length; i++) {
                System.out.println((i + 1) + ". " + keywords[i]);
            }
        } catch (IOException e) {
            // 打印异常信息
            e.printStackTrace();
        }
    }
}
最近更新