使用Java流,如何过滤集合,使结果包含所有元素,直到找到匹配项?

sai*_*sez 4 java java-8 java-stream

如果我有一个List包含对象

new MyObj("some"),
new MyObj("foo"),
new MyObj("bar"),
new MyObj("xyz");
Run Code Online (Sandbox Code Playgroud)

并希望在匹配某些条件使用Java 8流对其进行过滤,例如

myObj.prop = "foo";
Run Code Online (Sandbox Code Playgroud)

如何实现?

以上示例的结果应为

new MyObj("some"),
new MyObj("foo")
Run Code Online (Sandbox Code Playgroud)

那将是“传统”的方式:

List<MyObj> results = new ArrayList<>();
for (MyObj myObj : myObjList) {
  if (!myObj.prop.equals("foo")) {
    results.add(myObj);
  } else {
    results.add(myObj);
    break;
  }
}
Run Code Online (Sandbox Code Playgroud)

它不是谓词限制流的重复,因为它不包含匹配的元素。但是,我应该能够适应takeWhile操作。

Tag*_*eev 5

不幸的是,Stream API没有直接支持这种情况。甚至takeWhileJava 9中新出现的方法也无法解决此问题。一些免费的第三方Stream API扩展具有您需要的方法。例如,jOO中的limitWhileClosed吗?StreamEx中的takeWhileInclusive(我编写的库)。这是一个示例,如何通过StreamEx进行操作:

List<MyObj> results = StreamEx.of(myObjList)
    .takeWhileInclusive(myObj -> !myObj.prop.equals("foo")).toList();
Run Code Online (Sandbox Code Playgroud)

OO?版本看起来非常相似。

不使用第三方库,您可以通过两步来解决此问题:

int idx = IntStream.range(0, myObjList.size())
         .filter(i -> myObjList.get(i).prop.equals("foo"))
         .findFirst().orElse(myObjList.size());

List<MyObj> results = myObjList.stream().limit(idx+1).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

  • …或`results = idx == myObjList.size()?myObjList:myObjList.subList(0,idx + 1);`,取决于使用情况。 (2认同)