是否可以使用一行将流收集到两个不同的集合中?

时间:2015-12-07 13:03:35

标签: java java-stream

我有以下代码:(简化为简洁)

public void search(Predicate<String> predicate, Elements elements)
{
    List<SearchResult> searchResults = elements.stream()
        .filter(element -> predicate.test(element.ownText()))
        .map(element -> new SearchResult(element.ownText(), element.baseUri(),element.tagName()))
        .collect(Collectors.toList());
}

但是现在,我希望有另一个列表,其中包含没有映射的所有已过滤元素。 是否可以使用流来执行此操作,还是应该更改为foreach循环?

4 个答案:

答案 0 :(得分:4)

您可以在过滤后收集元素,然后使用此列表将元素映射到搜索结果:

public void search(Predicate<String> predicate, Elements elements) {
    List<Element> filteredElements = 
        elements.stream()
                .filter(element -> predicate.test(element.ownText()))
                .collect(Collectors.toList());

    List<SearchResult> searchResults = 
        filteredElements.stream()
                        .map(element -> new SearchResult(element.ownText(),element.baseUri(),element.tagName()))
                        .collect(Collectors.toList());
}

这不会比其他答案的解决方案花费更多时间,但没有副作用,线程安全且易于阅读和理解。

答案 1 :(得分:3)

执行此操作的简单方法是

List<Element> filteredElements = new ArrayList<>();
List<SearchResult> searchResults = elements.stream()
      .filter(element -> predicate.test(element.ownText()))
      .peek(filteredElements::add)
      .map(element -> 
         new SearchResult(element.ownText(),element.baseUri(),element.tagName()))
      .collect(Collectors.toList());

答案 2 :(得分:2)

另一种方法是定义public function showFilteredAction(\ActiView\CollectionPlan\Domain\Model\City $city) { $char = $this->request->getArgument('char'); /* This is pseudocode to demonstrate my idea */ foreach($city => street as $street){ /* Loop through the streets of the city model */ if(!$street => name.startsWith($char)){ /* Check if the streets name starts with the filter key */ $street.remove(); /* If it doesn't remove it from the city variable */ } } $this->view->assign('city', $city); $this->view->assign('char', $char); }

Supplier<Stream>

请注意,使用此方法实际上会执行两次过滤。

另一种选择(虽然在这种情况下不是很漂亮)解决方案是使用来自this answerpublic void search(Predicate<String> predicate, Elements elements) { Supplier<Stream<Element>> supplier = () -> elements.stream() .filter(element -> predicate.test(element.ownText())); List<SearchResult> searchResults = supplier.get() .map(element -> new SearchResult(element.ownText(),element.baseUri(),element.tagName())) .collect(Collectors.toList()); List<Element> elementList = supplier.get().collect(Collectors.toList()); } 收集器:

pairing

这些解决方案是通用的:它们允许使用单个数据源执行两种不同的操作。但是在您的具体示例中,更容易创建第一个已过滤的非映射数据列表,然后在该列表上创建第二个流:

Collector<Element, ?, List<SearchResult>> c1 = 
    Collectors.mapping(element -> new SearchResult(element.ownText(),element.baseUri(),element.tagName()),
        Collectors.toList());
Collector<Element, ?, List<Element>> c2 = Collectors.toList();
Collector<Element, ?, Pair<List<SearchResult>, List<Element>>> pairCollector =
    pairing(c1, c2, Pair::new); // Assumes some Pair class existing

Pair<List<SearchResult>, List<Element>> result = elements.stream()
           .filter(element -> predicate.test(element.ownText()))
           .collect(pairing);

答案 3 :(得分:0)

您无法使用该流两次,因为在使用collect时它已关闭。 以下代码将抛出“java.lang.IllegalStateException:stream已经被操作或关闭”。因此,您需要创建两次流或使用for-each循环。

import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Test
{
    static class SearchResult
    {
        SearchResult(Object o,Object p,Object q) {}
    }

    static class Element
    {
        public String ownText() {return "text";}
        public String tagName() {return "tag";}
        public String baseUri() {return "base";}
    }

    public static void search(Predicate<String> predicate, List<Element> elements)
    {
        Stream<Element> stream = elements.stream().filter(element -> predicate.test(element.ownText()));
        List<Element> filtered = stream.collect(Collectors.toList());
        // next line will throw the IllegalStateException as above line has already consumed the stream
        List<SearchResult> results =    stream.map(element -> 
         new SearchResult(element.ownText(),element.baseUri(),element.tagName()))
                .collect(Collectors.toList());
    }

    public static void main(String[] args)
    {       
        search(s->true,Collections.singletonList(new Element()));
    }
}