首页 > 解决方案 > 流过滤器中的 ContainsIgnoreCase 以计算字符串列表中出现的一个特定单词

问题描述

我想在 java 中的字符串列表中计算单个单词的出现次数。看似这项任务很容易,但我遇到了以大写字母开头或包含,.在单词末尾的单词的问题。我的方法看起来像:

public static Long countWordOccurence(List<String> wordList, String word) {

    return wordList.stream()
        .filter(s -> word.contains(s))
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
        .values()
        .stream()
        .findFirst()
        .orElse((long) -1);
  }

上面的代码在正常情况下工作正常,但问题出现在字符串末尾的 comaTest,或以大写字母开头的 String 之类的极端情况下。

我正在拆分我的字符串列表,例如:

Arrays.asList(TEXT_TO_PARSE.split(" ")); 

如果可能的话,我会很感激避免额外的依赖,但如果有必要,我不会鄙视。

我将不胜感激有关如何在流中修复我的过滤子句以正确计算字符串的建议。

标签: javajava-8functional-programmingjava-stream

解决方案


您的代码存在几个基本问​​题。

  • .filter(s -> word.contains(s))执行子字符串搜索。与您的问题标题相反,它不会忽略大小写。尽管如此,仍然可以有不同内容的字符串通过过滤器

  • .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))根据字符串的实际内容创建组。所以当多个不同的字符串通过前一个过滤器时,可能存在多个组

  • .values().stream().findFirst():由于groupingBy创建了一个未指定排序的地图,这将选择一个任意组。除此之外,这是一种非常低效的方式来要求count()

  • .orElse((long) -1)该值-1是一个非常奇怪的计数后备,因为当没有匹配时,最自然的答案是“零”。

所以一个直接的解决方案是

public static long countWordOccurence(List<String> wordList, String word) {
    return Collections.frequency(wordList, word);
}

用于计算区分大小写的匹配或

public static long countWordOccurence(List<String> wordList, String word) {
    return wordList.stream().filter(word::equalsIgnoreCase).count();
}

用于不区分大小写的计数。

但无论如何,这是一个xy 问题。

当您想计算字符串中某个单词的出现次数时,在执行实际搜索之前,无需将字符串拆分为单词并将数组转换为列表(顺便说一下,您可以直接流过数组)。

您可以使用

public static long countWordOccurence(String sentence, String word) {
    if(!word.codePoints().allMatch(Character::isLetter))
        throw new IllegalArgumentException(word+" is not a word");
    Pattern p = Pattern.compile("\\b"+word+"\\b");
    return p.matcher(sentence).results().count();
}

对于区分大小写的匹配计数和

public static long countWordOccurence(String sentence, String word) {
    if(!word.codePoints().allMatch(Character::isLetter))
        throw new IllegalArgumentException(word+" is not a word");
    Pattern p = Pattern.compile("\\b"+word+"\\b", Pattern.CASE_INSENSITIVE);
    return p.matcher(sentence).results().count();
}

对于不区分大小写的匹配。该\b模式表示单词边界,仅当搜索字符串实际上是一个单词时才有意义。因此,上述方法对此进行了预测试,这也确保了该单词不包含可能被误解为正则表达式模式的字符。

results()方法是在 Java 9 中引入的。此答案显示了在 Java 8 下创建此类流的解决方案,但是,对于诸如计数出现这样的简单任务,替代方法是在此处不使用流:

public static long countWordOccurence(String sentence, String word) {
    if(!word.codePoints().allMatch(Character::isLetter))
        throw new IllegalArgumentException(word+" is not a word");
    Pattern p = Pattern.compile("\\b"+word+"\\b", Pattern.CASE_INSENSITIVE);
    int count = 0;
    for(Matcher m = p.matcher(sentence); m.find(); count++) {}
    return count;
}

推荐阅读