使用流过滤java中的内部和外部列表

时间:2016-08-10 22:33:45

标签: java list java-8 java-stream

有没有办法使用一些谓词和java流过滤内部和外部列表?例如:

Class outerObject{
    int num;
    List<innerObject> innerObjectList;
}
Class innerObject{
    int num;
}
List <outerObject> outerObjectList

有没有办法过滤列表,outerObject列表包含字段num = 10,相应innerObject的{​​{1}}列表是还会按相应的字段outerObject过滤?

我试过了:

num = 10

但它似乎没有过滤内部列表。

描述性示例:

如果外部列表中有3个对象outerObjectList.stream() .filter(i -> i.getNum() == 10) .filter(i -> i.getOrders().stream().anyMatch(s -> getNum() == 10)) .collect(Collectors.toList()); ,并且3个对象中的每个对象在num = 10中都有1个内部对象innerObjectList那么它应该给我一个3 {{ 1}}列表中每个num = 10

4 个答案:

答案 0 :(得分:5)

它没有过滤内部列表,因为你在任何时候都没有改变任何元素;您只是将其从顶级列表中删除。

要过滤内部列表,您应该有一个对outerObjects进行操作的.map操作,将它们更改为新的outerObjects,其innerObjectLists本身就是旧对象的过滤版本。

outerObjectList.stream()
                .filter(i -> i.getNum() == 10)
              .filter(i -> i.getOrders().stream().anyMatch(s -> getNum() == 10))
                .collect(Collectors.toList());

让我们稍微分解一下:你有两个流,一个外部流(你在其上调用.filter.filter.collect,以及一个内部流,你调用.anyMatch。所有你最终得到的是外部流的集合,你决不修改它的任何成员(只有流本身的结构,通过删除它们。)你根本不修改内部流;你只是报告它的特征(&#34;它是否有getNum() == 10的任何内容?&#34;)

如果要过滤内部流,则还有更多工作要做。您实际上可以使用.peek修改外部流的元素,将其列表更改为&#34;已过滤&#34;版本;或者,如果您想要成为好的和不可变的,可以使用.map创建新的过滤版本。

对于.map()方法,您需要有一个lambda来创建一个新的OuterObject,以便:

  • output.num == input.num
  • output.innerObjectList == input.innerObjectList.stream().filter(s -> s.getNum() == 10)

这有意义吗?

答案 1 :(得分:0)

当你说&#39;过滤&#39;在流的上下文中,通常意味着过滤流,以便下游操作看到流的子集。我收集的比你真正想要的是根据条件从列表中删除项目。你可以用“偷看”来做到这一点。操作:

public class Outer {

    private final int num;
    private final List<Inner> inners = new ArrayList<>();

    public class Inner {

        private final int num;

        public Inner(int num) {
            this.num = num;
        }

        public boolean hasNum(int num) {
            return this.num == num;
        }
    }

    public Outer(int num) {
        this.num = num;
    }

    public void addInner(int num) {
        inners.add(new Inner(num));
    }

    public void removeInners(int num) {
        inners.removeIf(i -> i.hasNum(num));
    }

    public boolean hasInner(int num) {
        return inners.stream().anyMatch(i -> i.hasNum(num));
    }

    public boolean hasNum(int num) {
        return this.num == num;
    }
}

@Test
public void testOuter() {
    List<Outer> outers = new ArrayList<>();
    outers.add(new Outer(1));
    outers.add(new Outer(1));
    outers.get(1).addInner(5);
    outers.get(1).addInner(1);
    outers.add(new Outer(2));
    outers.add(new Outer(1));
    outers.get(3).addInner(1);
    outers = outers.stream()
            .filter(o -> o.hasNum(1))
            .peek(o -> o.removeInners(1))
            .collect(Collectors.toList());
    assertThat(outers.size(), is(2));
    assertFalse(outer.stream().anyMatch(i -> i.hasInner(1)));
}

请注意,我收集后将列表分配回变量。

答案 2 :(得分:0)

如果要就地修改列表(不创建新列表),可以使用tutorial2方法:

outerObjectList.removeIf(i -> i.getNum() != 10);
outerObjectList.forEach(i -> i.getOrders().removeIf(s -> s.getNum() != 10));

此操作不需要Stream API。

答案 3 :(得分:0)

outerObjectList.stream()
            .filter(outer -> outer.num == 10)
            .map(outerObject -> {
                outerObject.innerObjectList= outerObject.innerObjectList.stream().filter(innerObject -> innerObject.num == 10).collect(Collectors.toList());
                return outerObject;
            })
            .collect(Collectors.toList());