【问题标题】:filtering a stream against items in another list针对另一个列表中的项目过滤流
【发布时间】:2017-01-29 14:13:49
【问题描述】:

尝试filter 一个流对不同列表中的数据:

它有效,但我在流中间使用了for 循环。我找不到任何有关如何将 for 循环转换为流的信息。

我可以只 .stream() selction.getItems() 而不是 .forEach() 并拥有一个新的 .stream() DATA.accounts,但这是糟糕的代码,因为它必须在每个 .forEach. 上重新传输

        y=1;

        DATA.accounts.stream()
            .flatMap(estimate -> estimate.getElements().stream())
            .filter( ele-> {

                // different list;
                for (Element element:selection.getItems()){
                    if (element.getId()==ele.getId()){
                        return true;
                    }
                }
                return false;
            })

            .forEach(element -> {
                element.setDateSchedualed(selectedDate);
                element.setOrder(y);    
                y++;
            });

【问题讨论】:

  • for-each 中的return false 是故意存在的吗?就像您只想检查第一个元素一样?
  • @Calculator 不,不应该存在错误。编辑了。
  • 改变y 变量的代码根本不起作用,这比filter 中的循环更让你烦恼...

标签: java for-loop java-8 java-stream


【解决方案1】:

我认为你真正需要的是:

list1.removeAll(list2);

但不涉及任何流。

【讨论】:

    【解决方案2】:

    你可以将filter表示为

    .filter(ele -> selection.getItems().stream()
                            .anyMatch(element -> element.getId()==ele.getId())
    

    这个“必须重新流式传输”这一事实不应该比原始代码将为每个元素循环的事实更让您烦恼。无论哪种情况,您都创建了具有O(n×m) 时间复杂度的操作。如果您可以肯定地预测其中一个列表总是非常小,这是可以接受的。

    否则,无法通过将 id 值存储在具有快速(最佳情况下为O(1))查找的结构中来准备此操作。即

    Set<IdType> id = selection.getItems().stream()
        .map(element -> element.getId())
        .collect(Collectors.toSet());
    
    …
    
    .filter(ele -> id.contains(ele.getId())
    

    除此之外,当y 是一个局部变量时,你的forEach 增加y 变量的方法显然是一种反模式,甚至无法编译。如果y 是一个字段,它会使这段代码变得更糟。在这里,接受一个临时存储到List 中要干净得多:

    Set<IdType> id = selection.getItems().stream().map(element -> element.getId());
    
    List<ElementType> list = DATA.accounts.stream()
        .flatMap(estimate -> estimate.getElements().stream())
        .filter(ele -> id.contains(ele.getId())
        .collect(Collectors.toList());
    
    IntStream.range(0, list.size())
        .forEach(ix -> {
            ElementType element = list.get(ix);
            element.setDateSchedualed(selectedDate);
            element.setOrder(ix+1);
        });
    

    【讨论】:

      【解决方案3】:

      将其他列表的 ID 放入 Set selectedIds,然后根据 ele-&gt; selectedIds.contains(ele.getId()) 进行过滤。

      这会给你(摊销的)线性时间复杂度。

      由于您需要为流中的每个项目检查 selected 中的所有元素是否存在,我不认为会有任何直接使用流的方法(因为您不能真正为此流式传输 selected 集合任务)。

      【讨论】:

        【解决方案4】:

        如果您想在线性时间内搜索 id,我认为使用 for-each 循环实际上没有任何问题,因为例如,如果您的项目列表是 ArrayList 并且您使用了它的 contains 方法过滤,它实际上也只是循环遍历元素。您可以编写一个通用的包含函数,例如:

        public static <E1, E2> boolean contains(Collection<E1> collection, E2 e2, BiPredicate<E1, E2> predicate){
            for (E1 e1 : collection){
                if (predicate.test(e1, e2)){
                    return true;
                }
            }
            return false;
        }
        

        并用它替换你的 for-each 循环:

        ele -> contains(selection.getItems(), ele, (e1, e2) -> e1.getId() == e2.getId())
        

        【讨论】:

          猜你喜欢
          • 2015-06-04
          • 2011-10-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-13
          • 2021-12-17
          • 2016-07-14
          相关资源
          最近更新 更多