【问题标题】:filter inner and outer list in java using streams使用流过滤java中的内部和外部列表
【发布时间】:2016-08-10 22:33:45
【问题描述】:

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

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

有没有办法过滤列表,使outerObject 列表具有字段num = 10,并且对应outerObjectinnerObject 列表也被相应字段num = 10 过滤?

我试过了:

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

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

描述性示例:

如果一个外部列表有 3 个带有 num = 10 的对象,并且 3 个对象中的每一个在它们的 innerObjectList 中有 1 个带有 num = 10 的内部对象,那么它应该给我一个列表 3 outerObject 和一个列表 1 innerObject每个。

【问题讨论】:

  • "描述性示例:如果一个外部列表有 3 个 num = 10 的对象,并且 3 个对象中的每一个在其 innerList 中有 1 个内部对象,其 num =10 那么它应该给我一个 3 个外部对象的列表每个都有一个包含 1 个内部对象的列表。”当您还描述它实际在做什么时,描述性示例会更好。 :) 另外,在这种情况下,它根本不应该修改任何东西?
  • 如果我让您感到困惑,我深表歉意。我只想根据谓词过滤内部列表,而外部列表也根据使用流的某些谓词进行过滤。
  • 所以,要明确一点:如果 OuterObject 包含混合的 InnerList,您想使用 Streams 来保留 Outer Object,但将 InnerList 更改为过滤后的版本,对吗?
  • @EdwardPeters 是的,没错。同时还要确保外部对象也符合标准。
  • 是的。请参阅我的答案(现在我确定我们在同一页面上,我将进行更详细的编辑。)

标签: java list java-8 java-stream


【解决方案1】:

如果您想就地修改列表(不创建新列表),可以使用Collection.removeIf 方法:

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

此操作不需要 Stream API。

【讨论】:

  • 如果有三层列表会发生什么 .outerObjectList.forEach(i -> i.getOrders().forEach(s -> s.innerOrders().removeif(o -> o.getnum () == 10) 似乎不起作用。
【解决方案2】:

它不会过滤内部列表,因为您绝不会更改任何元素;您只是将它们从顶级列表中删除。

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

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

让我们稍微分解一下:您有两个流,一个外部流(您在其上调用.filter.filter.collect,以及一个内部流,您在其上调用.anyMatch。所有这些你最终得到的是外部流的集合,并且在任何时候你都不会修改它的任何成员(只有流本身的结构,通过删除它们。)你根本不修改内部流;你只是报告它的一个特征(“它与getNum() == 10 有什么关系吗?”)

如果要过滤内部流,还有更多工作要做。您实际上可以使用.peek 修改外部流的元素,将它们的列表更改为“过滤”版本;或者,如果您想保持良好且不可变,您可以使用.map 创建新的过滤版本。

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

  • output.num == input.num
  • output.innerObjectList == input.innerObjectList.stream().filter(s -&gt; s.getNum() == 10)

这有意义吗?

【讨论】:

    【解决方案3】:

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

    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)));
    }
    

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

    【讨论】:

    • 这不是过滤内部列表 - 它是根据内部列表的内容过滤外部列表。 ((Bad, Good), (Good, Good), (Bad, Bad)) -&gt; ((Bad, Good), (Good, Good)) 而不是 ((Bad, Good), (Good, Good), (Bad, Bad)) -&gt; ((Good), (Good, Good), ())
    • 是的。这也是我的代码所做的。它只过滤外部列表。不是内部列表。我也需要它来过滤内部列表
    • 我已更改代码以从内部列表中删除项目。我假设这就是你的意思。
    【解决方案4】:
    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());
    

    【讨论】:

      猜你喜欢
      • 2019-11-24
      • 1970-01-01
      • 1970-01-01
      • 2019-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-19
      • 2011-12-15
      相关资源
      最近更新 更多