当前位置: 首页 > 知识库问答 >
问题:

流:筛选子项,返回父项

卫增
2023-03-14

假设一个类MyClass:

public class MyClass {

    private final Integer myId;
    private final String myCSVListOfThings;

    public MyClass(Integer myId, String myCSVListOfThings) {
        this.myId = myId;
        this.myCSVListOfThings = myCSVListOfThings;
    }

    // Getters, Setters, etc
}

这条流:

final Stream<MyClass> streamOfObjects = Stream.of(
        new MyClass(1, "thing1;thing2;thing3"),
        new MyClass(2, "thing2;thing3;thing4"),
        new MyClass(3, "thingX;thingY;thingZ"));

我想返回myCSVListOfThings中包含条目"thing2"MyClass的每个实例

如果我想要一份清单

List<String> filteredThings = streamOfObjects
        .flatMap(o -> Arrays.stream(o.getMyCSVListOfThings().split(";")))
        .filter("thing2"::equals)
        .collect(Collectors.toList()); 

但我真正需要的是一个列表

这就是我现在拥有的:

List<MyClass> filteredClasses = streamOfObjects.filter(o -> {
     Stream<String> things = Arrays.stream(o.getMyCSVListOfThings().split(";"));
     return things.anyMatch(s -> s.equals("thing2"));
}).collect(Collectors.toList());

但不知怎的,这感觉不太对。除了在谓词中打开一个新的流之外,还有什么更清洁的解决方案吗?

共有3个答案

百里飞捷
2023-03-14

在我看来,你有三个选择。

1) 在字符串中查找特定条目而不拆分它-仍然看起来很混乱

List<MyClass> filteredClasses = streamOfObjects
              .filter(o -> o.getMyCSVListOfThings().contains(";thing2;"))
              .collect(Collectors.toList());

2) 地图两次-仍然凌乱

List<MyClass> filteredClasses = streamOfObjects
              .map(o -> Pair<MyClass, List<String>>.of(o, toList(o.getMyCSVListOfThings()))
              .filter(pair -> pair.getRight().contains("thing2"))
              .map(pair -> pair.getLeft())
              .collect(Collectors.toList());

其中toList是一个将String转换为List的方法

3) 创建其他字段-我建议的方法

扩展类MyClass-向类添加字段

List<String> values;

并在构造函数中初始化它:

public MyClass(Integer myId, String myCSVListOfThings) {
    this.myId = myId;
    this.myCSVListOfThings = myCSVListOfThings;
    this.values = toList(myCSVListOfThings);
}

然后在溪流中简单地:

List<MyClass> filteredClasses = streamOfObjects
          .filter(o -> o.getValues().contains("thing2"))
          .collect(Collectors.toList());

当然,如果需要,可以在第一次getValues方法调用期间以延迟模式初始化字段值。

易阳云
2023-03-14

一种解决方案是使用避免拆分和流操作的模式匹配:

Pattern p=Pattern.compile("(^|;)thing2($|;)");
List<MyClass> filteredClasses = streamOfObjects
    .filter(o -> p.matcher(o.getMyCSVListOfThings()).find())
    .collect(Collectors.toList());

由于参数为字符串。split定义为正则表达式模式,上面的模式与在split的结果中查找匹配项具有相同的语义;您正在两个边界之间查找单词thing2,第一个是行的开头或分号,第二个是行的结尾或分号。

除此之外,在谓词中使用另一个流操作也没有错。但是有一些方法可以改进它。如果省略保存流的过时局部变量,lambda表达式会变得更简洁。通常,您应该避免将流实例保存在局部变量中,因为直接链接操作将减少尝试多次使用流的风险。其次,您可以使用Pattern类对分割操作的结果元素进行流式处理,而无需首先将它们全部收集到一个数组中:

Pattern p=Pattern.compile(";");
List<MyClass> filteredClasses = streamOfObjects
    .filter(o -> p.splitAsStream(o.getMyCSVListOfThings()).anyMatch("thing2"::equals))
    .collect(Collectors.toList());

Pattern p=Pattern.compile(";");
List<MyClass> filteredClasses = streamOfObjects
    .filter(o -> p.splitAsStream(o.getMyCSVListOfThings()).anyMatch(s->s.equals("thing2")))
    .collect(Collectors.toList());

请注意,您还可以将原始代码重写为

List<MyClass> filteredClasses = listOfObjects.stream()
    .filter(o -> Arrays.asList(o.getMyCSVListOfThings().split(";")).contains("thing2"))
    .collect(Collectors.toList());

现在,谓词中的操作不是Stream而是Collection操作,但这不会改变语义或代码的正确性......

奚修伟
2023-03-14

首先,我建议您向MyClass添加额外的方法,以便您可以像这样转换代码:

List<MyClass> filteredClasses = streamOfObjects
  .filter(o -> o.containsThing("thing2"))
  .collect(Collectors.toList());

现在您可以根据输入数据实现此方法:拆分为Stream,拆分为Set,甚至搜索substring(如果可能并且有意义),如果需要,缓存结果。

你们对这个类的用法了解得更多,所以你们可以做出正确的选择。

 类似资料:
  • 问题内容: 使用Java Stream时,映射后有时会出现空值。当前,当这些值需要省略时,我使用: 对于更实用的样式,可以快速编写一个小的辅助方法: 这样您就可以使用方法引用了: 我找不到这样的jdk方法,即使我怀疑它们中已经包含了一种。这里有其他方法吗?还是他们出于某种原因忽略了这一点? 问题答案: 您可以从Java8 SDK 使用Objects :: nonNull :

  • 注意:我实际上不确定为什么这种情况首先需要监听器,因为我甚至不想经常监听,而是在调用方法时得到结果。

  • 我有一个带有键的HashMap,值是字符串。我想通过以字符串“locationid”开头的键值过滤HashMap,并将键中的值返回到字符串数组列表中。HashMap的填充方式如下: 我需要arraylist中的ORG_Id值。 我找不到可以将值放入字符串列表的位置。编译错误是它不识别values()方法。 更新还尝试将筛选后的Hashmap放入另一个Hashmap中,如下所示: 但得到的编译错误是

  • 我希望,如果我选择“mammals”,动物选择选项只显示值为1的选项data-animal_class。 我知道如何获得哺乳动物值,但我不知道如何使用过滤器 这是我的代码:

  • 我的spring boot应用程序配置为Reactive。但是我的REST APIendpoint配置为返回ResponseEntity而不使用Mono或flux。 我想实现过滤器,这将检查每个endpoint的传入头。旧的Servlet过滤器当然不起作用,新的WebFilter对webflux和其他过滤器也不起作用。

  • 我写了示例代码,但它不起作用。还观察到2个选项卡只有1个窗口句柄。如何再次切换到父页签?