用迭代器和收集整理流?

时间:2015-05-28 15:16:44

标签: java java-8 java-stream

说,我想过滤一个列表并返回已过滤的列表,但迭代器也足够了。以下哪个选项更可取?为什么? Stream.iterator()Stream.collect(ListCollector)

2 个答案:

答案 0 :(得分:13)

Stream.iterator().collect(Collectors.toList()) .iterator()之间存在根本区别。后者将处理流的所有项目,以便将它们存储到集合中。相比之下,Stream.iterator()只会在Stream的{​​{1}}周围返回一个包装器,它将像所有其他流操作一样懒惰地处理所有项目。

E.g。当你写作

Spliterator

它会打印出来:

Iterator<String> it=IntStream.range(0, 100).mapToObj(i->{
    System.out.println("processing "+i);
    return String.valueOf(i);
}).iterator();

if(it.hasNext()) System.out.println("first: "+it.next());
if(it.hasNext()) System.out.println("second: "+it.next());
return;// I don’t care about the remaining values

,而

processing 0
first: 0
processing 1
second: 1

将打印

Iterator<String> it=IntStream.range(0, 100).mapToObj(i->{
    System.out.println("processing "+i);
    return String.valueOf(i);
}).collect(Collectors.toList()).iterator();

if(it.hasNext()) System.out.println("first: "+it.next());
if(it.hasNext()) System.out.println("second: "+it.next());
return;// I don’t care about the remaining values

也就是说,如果你需要的只是processing 0 processing 1 processing 2 processing 3 processing 4 processing 5 processing 6 processing 7 processing 8 processing 9 processing 10 … processing 90 processing 91 processing 92 processing 93 processing 94 processing 95 processing 96 processing 97 processing 98 processing 99 first: 0 second: 1 ,你应该在请求之前强制收集值,除非你有充分的理由这样做(例如,如果来源的话)是一个文件,您可能希望在返回迭代器之前完成操作。)

答案 1 :(得分:4)

您没有提供它作为替代方案之一,但我建议您考虑将Stream返回给来电者。如果您愿意将Iterator返回给调用者,则Stream可能更方便。 (Jean Logeart也在评论中提出了这一点。)

听起来你有一个内部的项目集合(或其他),并且您正在使用流来按照您不希望向调用者公开的某些条件对其进行过滤。给定一个过滤流,有几个替代方法可以返回:

  1. a List:stream.collect(toList())
  2. 来自流的迭代器:stream.iterator()
  3. 来自收集列表的迭代器:stream.collect(toList()).iterator()
  4. 流本身:stream
  5. 哪个最好取决于调用者想要对返回值做什么。如果您知道,调用者总是希望存储所有已过滤的项目,您也可以通过自己收集这样的列表来帮助调用者。

    然而,呼叫者可能想要做一些不同的事情。假设调用者想要查找特定项目,或者计算过滤项目的数量,或者查看是否有任何过滤项目。在这些情况下,将项目收集到列表中主要是浪费。

    从过滤后的流中返回迭代器会让您感到懒惰,因为该集合未预先创建。但是,处理迭代器对于调用者来说可能很麻烦。

    如果调用者想要一个集合,那么这样的东西就是必要的:

    Iterator<Item> iter = getFilteredItems();
    List<Item> result = new ArrayList<>();
    iter.forEachRemaining(result::add);
    

    如果来电者想要查找某个特定项目,情况会更糟:

    Item foundItem = null;
    while (iter.hasNext()) {
        Item current = iter.next();
        if (targetId.equals(current.getId())) {
            foundItem = current;
            break;
        }
    }
    if (foundItem != null) {
        // found it!
    } else {
        // not found
    }
    

    计算过滤项目的数量可以在while (iter.hasNext())循环中递增计数器,也可以在AtomicInteger内递增forEachRemaining。幸运的是,测试是否存在任何已过滤的项目非常简单,只需调用iter.hasNext()

    在收集的列表上返回迭代器是两个世界中最糟糕的。即使调用者不需要,您也需要支付收集List的前期费用,并且调用者可能需要做额外的工作来运行所有元素,如上所示。 Holger已经很好地解释了这些差异。

    最后,返回Stream提供了懒惰的效率,可能是最灵活的。如果您已经使用流过滤,只需返回流:

    Stream<Item> getFilteredItems() {
        return myInternalCollection.stream()
                                   .filter(...);
    }
    

    如果来电者想要一个项目列表,那很简单:

    List<Item> = getFilteredItems().collect(toList());
    

    如果来电者想要找到特定的项目,那也很简单:

    Optional<Item> item = getFilteredItems()
                            .filter(it -> targetId.equals(it.getId()))
                            .findAny();
    if (item.isPresent()) {
        // found it!
    } else {
        // not found
    }
    

    Optional类本身有一组丰富的API,可以让您避免ifPresent()测试。)

    计算项目只是:

    long count = getFilteredItems().count();
    

    并测试是否有任何过滤的项目:

    boolean any = getFilteredItems().findAny().isPresent();
    

    返回流可以尽可能长时间地保持懒惰,并为调用者提供最大的灵活性。