filtering a stream against items in another list

不打扰是莪最后的温柔 提交于 2021-02-04 07:49:48

问题


trying to filter a stream against data within a different list:

It works, but I use a for loop in the middle of the stream. I cannot find any information of how to convert the for loop to a stream.

I could just .stream() the selction.getItems() than .forEach() and have a new .stream() of DATA.accounts, but that is poor code as it would have to restream on every .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++;
            });

回答1:


You can express the filter as

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

The fact that this “would have to restream” shouldn’t bother you more than the fact that the original code will loop for every element. You have created an operation with O(n×m) time complexity in either case. This is acceptable if you can surely predict that one of these lists will always be very small.

Otherwise, there is no way around preparing this operation by storing the id values in a structure with a fast (O(1) in the best case) lookup. I.e.

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

…

.filter(ele -> id.contains(ele.getId())

Besides that, your forEach approach incrementing the y variable clearly is an anti-pattern and it doesn’t even compile, when y is a local variable. And if y is a field, it would make this code even worse. Here, it’s much cleaner to accept a temporary storage into a 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);
    });



回答2:


I think what you really need is:

list1.removeAll(list2);

No streams involved though.




回答3:


Put the other list's IDs in a Set selectedIds, then filter based on ele-> selectedIds.contains(ele.getId()).

That will give you (amortized) linear time complexity.

Since you need to check presence among all elements in selected for each item in the stream, I don't expect there will be any straightforward method using only streams (because you cannot really stream the selected collection for this task).




回答4:


I think there is actually nothing wrong with using a for-each loop if you want to search for the id in linear time, because for example if your items list was an ArrayList and you used its contains method for filtering, it would actually also just loop over the elements. You could write a general contains function like:

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

and replace your for-each loop with it:

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


来源:https://stackoverflow.com/questions/41921835/filtering-a-stream-against-items-in-another-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!