【问题标题】:Java Stream API to find words containing a list of charactersJava Stream API 查找包含字符列表的单词
【发布时间】:2021-01-15 03:58:53
【问题描述】:

我正在学习 Java 8,我正在尝试减少对 for 循环的使用,并使我的代码看起来更简洁。我正在尝试找出解决以下问题的方法。 我有一个单词列表和一个字符数组,例如:

 List<String> words = new ArrayList<>();
     words.add("go");
     words.add("me");
     words.add("boy");
     words.add("ate");
     words.add("mate"); 
     words.add("goal");

 char[] required_chars =  {'e', 'o', 'b', 'a', 'm', 'g', 'l'} ;

输出应该是 (go, me, goal),因为这些单词包含 required_chars 数组中的字符/字母。它应该只严格使用数组中的元素。 我怎样才能做到这一点?

【问题讨论】:

  • 您确实需要尝试一些代码,我认为不太可能有人会为您编写整个代码。
  • @markspace 我试图检查每个字符是否包含在列表的每个元素中。但如果他们所有人都可以一起将这个词作为输出输出,我无法实现。
  • 最简单的:words.stream().filter(s -&gt; s.matches("["+new String(required_chars)+ "]+")) .forEach(System.out::println);如果要避免不必要的重复工作,可以使用Pattern p = Pattern.compile("["+new String(required_chars)+ "]+"); words.stream().filter(s -&gt; p.matcher(s).matches()).forEach(System.out::println);,从Java 11开始,可以使用.filter(p.asMatchPredicate())代替.filter(s -&gt; p.matcher(s).matches())

标签: java lambda java-8 java-stream


【解决方案1】:
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class FilterListStreamWithCharArray {

    public static void main(String[] args) {

        List<String> words = new ArrayList<>();
        words.add("go");
        words.add("me");
        words.add("boy");
        words.add("ate");
        words.add("mate");
        words.add("goal");

        char[] required_chars = { 'e', 'o', 'b', 'a', 'm', 'g', 'l' };

        Set<Character> requiredCharsAsSet = getBoxedChars(required_chars);

        List<String> filteredWords = words.stream().filter(eachElement -> {

            char[] eachElementsCharArray = eachElement.toCharArray();

            Set<Character> eachElemenstCharAsSet = getBoxedChars(eachElementsCharArray);

            if (requiredCharsAsSet.containsAll(eachElemenstCharAsSet)) {
                return true;
            }

            return false;
        }).collect(Collectors.toList());

        System.out.println(filteredWords);

    }

    private static Set<Character> getBoxedChars(char[] charArray) {

        Set<Character> resultSet = new HashSet<>();

        for (int i = 0; i < charArray.length; i++) {
            resultSet.add(new Character(charArray[i]));
        }
        return resultSet;
    }
}

【讨论】:

    【解决方案2】:

    这是我对简单 Java String 和 Stream api 的尝试

    class Scratch
    {
    
        public static void main(String[] args)
        {
            List<String> words = new ArrayList<>();
            words.add("go");
            words.add("me");
            words.add("boy");
            words.add("ate");
            words.add("mate");
            words.add("goal");
    
            List<String> result = words.stream()
                                        .filter(word -> isMatch(word))
                                        .collect(Collectors.toList());
            System.out.println(result);
        }
    
        static boolean isMatch(String word) {
            Character[] required_chars =  {'e', 'o', 'b', 'a', 'm', 'g', 'l'} ;
            long count = Arrays.stream(required_chars)
                               .filter(ch -> word.contains(ch.toString()))
                               .count();
            return count == word.length();
        }
    }
    

    【讨论】:

      【解决方案3】:

      这是一种基于正则表达式的方法:

      String[] requiredChars =  {"e", "o", "b", "a", "m", "g", "l"};
      String regex = "[^" + String.join("", requiredChars) + "]";
      List<String> words = Arrays.asList(
          new String[] { "go", "me", "boy", "ate", "mate", "goal"
      });
      List<String> matches = words.stream()
          .filter(x -> !x.matches(".*" + regex + ".*"))
          .collect(Collectors.toList());
      System.out.println(matches);
      

      打印出来:

      [go, me, goal]
      

      解释:

      我们形成了字母表中各个字符的正则表达式交替,这些字符出现在任何匹配项中:

      [^eobamgl]
      

      然后,我们可以流式传输您输入的单词列表,并删除与这些黑名单中的一个或多个字母匹配的任何字符串。

      【讨论】:

      • 您可以简单地使用[eobamgl] 而不是(?:e|o|b|a|m|g|l)。您可以使用matches("[eobamgl]+") 检查字符串是否完全由指定字符组成,而不是使用否定。将它们放在一起使this one-liner resp。如果您不想为每个匹配操作再次组合和解析表达式,则使用双线。我不确定这是否是一种“大量基于正则表达式”的方法;这是相当基本的正则表达式用法……
      • @Holger 感谢您的出色反馈,我已经按照您的建议进行了更改。当我发布最初的答案时,我想我是在喝第一杯咖啡。
      • 我仍然不明白你为什么坚持双重否定,而不是测试matches("[eobamgl]+")(或[eobamgl]*,如果你想接受空字符串)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-17
      • 2016-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多