Skip to main content
 首页 » 编程设计

Java检查字符串包含多个关键词

2022年07月19日140leader

Java检查字符串包含多个关键词

本文介绍如何在字符串中监测多个关键词。

1. 需求说明

String inputString = "hello there, java"; 

我们的任务是在 inputString中查找是否有 hellojava 两个单词。把要查找的关键词放入数组中:

String[] words = {
   "hello", "java"}; 

查找的顺序不重要,但应该需要大小写敏感。

2. 传统方法

2.1 String.contains()

我们首先使用 String.contains()方法实现,循环关键词数组依次监测:

public static boolean containsWords(String inputString, String[] items) {
    
    boolean found = true; 
    for (String item : items) {
    
        if (!inputString.contains(item)) {
    
            found = false; 
            break; 
        } 
    } 
    return found; 
} 

如果包括特定关键词contains()方法返回 true。只要有一个关键词不被包括,则结束循环并返回 false。虽然这个方法需要代码较多,但它是最简单也是最快的方法。

2.2 String.indexOf()

与上面方法类似,我们可以使用 indexOf方法监测关键词的位置。实现如下:

public static boolean containsWordsIndexOf(String inputString, String[] words) {
    
    boolean found = true; 
    for (String word : words) {
    
        if (inputString.indexOf(word) == -1) {
    
            found = false; 
            break; 
        } 
    } 
    return found; 
} 

如果监测到 indexOf()具体位置,否则返回 -1

2.3 使用正在表达式

下面我们使用正则表达式实现,因此需要使用 Pattern类。首先我们定义字符串表达式,因为需要匹配两个关键词,我们使用向前匹配模式:

Pattern pattern = Pattern.compile(**"(?=.\*hello)(?=.\*java)"**); 

通用写法:

StringBuilder regexp = new StringBuilder(); 
for (String word : words) {
    
    regexp.append("(?=.*").append(word).append(")"); 
} 

然后使用 matcher()方法进行监测:

public static boolean containsWordsPatternMatch(String inputString, String[] words) {
    
  
    StringBuilder regexp = new StringBuilder(); 
    for (String word : words) {
    
        regexp.append("(?=.*").append(word).append(")"); 
    } 
  
    Pattern pattern = Pattern.compile(regexp.toString()); 
    // Pattern pattern = Pattern.compile(regexp.toString(), Pattern.CASE_INSENSITIVE); 
     
    return pattern.matcher(inputString).find(); 
} 

但是正则有性能花销,如果要查找多个关键词,该方法不是最优方案。

3. Java8 和 List

我们使用Java 8 的 流 API实现,首先对前面的数据进行转换:

List<String> inputString = Arrays.asList(inputString.split(" ")); 
List<String> words = Arrays.asList(words); 

下面使用流API:

public static boolean containsWordsJava8(String inputString, String[] words) { 
    List<String> inputStringList = Arrays.asList(inputString.split(" ")); 
    List<String> wordsList = Arrays.asList(words); 
  
    return wordsList.stream().allMatch(inputStringList::contains); 
} 

如果源字符串包括所有关键词,则上面管道操作返回true。部分匹配可以使用 anyMatch方法。

另外完全匹配也可以使用 Collection中的 containsAll方法实现:

public static boolean containsWordsArray(String inputString, String[] words) {
    
    List<String> inputStringList = Arrays.asList(inputString.split(" ")); 
    List<String> wordsList = Arrays.asList(words); 
  
    return inputStringList.containsAll(wordsList); 
} 

4. 使用AC算法(*Aho-Corasick* )

简言之,AC算法在字符串中查找多个关键词,算法复杂度为 O(n) ,与多少个关键词或长度没有关系。

增加依赖:

<dependency> 
    <groupId>org.ahocorasick</groupId> 
    <artifactId>ahocorasick</artifactId> 
    <version>0.4.0</version> 
</dependency> 

首先使用关键词数组构建Trie管道,使用 Trie数据结构:

Trie trie = Trie.builder().onlyWholeWords().addKeywords(words).build(); 

然后传入 inputString参数 调用解析方法并保存结果至 emits集合中:

Collection<Emit> emits = trie.parseText(inputString); 

最后打印结果:

emits.forEach(System.out::println); 

对应每个关键,如果查找到,则打印开始、结束位置以及关键词自身。

完整代码:

public static boolean containsWordsWithAC(String inputString, String[] words) {
    
    Trie trie = Trie.builder().onlyWholeWords().addKeywords(words).build(); 
  
    Collection<Emit> emits = trie.parseText(inputString); 
    emits.forEach(System.out::println); 
  
    boolean found = true; 
    for(String word : words) {
    
        boolean contains = Arrays.toString(emits.toArray()).contains(word); 
        if (!contains) {
    
            found = false; 
            break; 
        } 
    } 
  
    return found; 
} 

上面示例仅查找完整关键词。如果你还需要如 hellojava,那仅需要删除 onlyWholeWords方法。

5. 总结

本文介绍了多种方法实现在字符串中查找多个关键词方法。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/108265666
阅读延展