Java检查字符串包含多个关键词
本文介绍如何在字符串中监测多个关键词。
1. 需求说明
String inputString = "hello there, java";
我们的任务是在 inputString
中查找是否有 hello
和 java
两个单词。把要查找的关键词放入数组中:
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. 总结
本文介绍了多种方法实现在字符串中查找多个关键词方法。