我应该返回一个集合或stream?

假设我有一个将只读视图返回给成员列表的方法:

class Team { private List<Player> players = new ArrayList<>(); // ... public List<Player> getPlayers() { return Collections.unmodifiableList(players); } } 

进一步假设所有客户端都立即迭代列表一次。 也许把玩家放入JList或其他东西。 客户端不存储对列表的引用以供以后检查!

鉴于这种常见的情况,我应该返回一个stream吗?

  public Stream<Player> getPlayers() { return players.stream(); } 

或者在Java中返回一个非惯用的stream? stream被devise为始终被“终止”在他们创build的相同expression式内?

答案是一如既往,“要看情况”。 这取决于返回的集合有多大。 这取决于结果是否随时间变化,以及返回结果的一致性如何。 这很大程度上取决于用户如何使用答案。

首先,请注意,您始终可以从stream中获取集合,反之亦然:

 // If API returns Collection, convert with stream() getFoo().stream()... // If API returns Stream, use collect() Collection<T> c = getFooStream().collect(toList()); 

所以问题是,这对您的来电者更有用。

如果你的结果可能是无限的,那么只有一个select:stream。

如果你的结果可能非常大,你可能更喜欢Stream,因为一次实现它可能没有任何价值,这样做可能会造成很大的堆压力。

如果所有的调用者要做的都是迭代(search,过滤,聚合),你应该更喜欢Stream,因为Stream已经内置了这些内容,并且不需要实现一个集合(特别是如果用户可能不处理整个结果)。这是一个很常见的情况。

即使你知道用户会迭代多次或者保留它,你仍然可能想要返回一个Stream,简单的事实是,无论你select哪个Collection(例如,ArrayList)可能不是他们想要的forms,然后来电者必须复制它。 如果你返回一个stream,他们可以collect(toCollection(factory))并以他们想要的forms得到它。

上面的“喜欢Stream”案例大多来自Stream更灵活的事实; 你可以迟迟地使用它,而不会招致成本和限制。

必须返回集合的一种情况是,当存在较强的一致性要求时,必须为移动的目标生成一致的快照。 然后,你将要把元素放入一个不会改变的集合中。

所以我想说大多数情况下,Stream是正确的答案 – 它更加灵活,不会强加通常不必要的物化成本,并且可以根据需要轻松地将其转换为您select的集合。 但是有时候,你可能不得不返回一个Collection(比如说,由于一致性要求很高),或者你可能想返回Collection,因为你知道用户将如何使用它,并且知道这对他们来说是最方便的。

我有几点要补充给布赖恩·戈茨的优秀答案 。

从“getter”风格的方法调用中返回Stream是相当常见的。 请参阅Java 8 javadoc中的“ stream使用情况”页面 ,并查找java.util.Stream以外的包的“方法…返回stream”。 这些方法通常是表示或可以包含多个值或聚合的类。 在这种情况下,API通常会返回它们的集合或数组。 由于Brian在回答中提到的所有原因,在这里添加Stream-returning方法非常灵活。 许多这些类已经有了集合或数组返回的方法,因为这些类在Streams API之前。 如果您正在devise一个新的API,并且提供stream返回方法是有意义的,那么也可能不需要添加收集返回方法。

Brian提到了将这些价值“物化”成一个集合的成本。 为了扩大这一点,实际上有两个成本:在集合中存储值的成本(内存分配和复制)以及创build值的成本。 后者的成本通常可以通过利用Stream的懒惰寻求行为来减less或避免。 一个很好的例子是java.nio.file.Files中的API:

 static Stream<String> lines(path) static List<String> readAllLines(path) 

readAllLines不仅需要将整个文件内容保存在内存中以便将其存储到结果列表中,还必须在文件返回列表之前将文件读到最后。 lines方法可以在执行一些设置之后立即返回,直到稍后需要的时候才将文件读取和换行,或者根本不需要。 这是一个巨大的好处,例如,如果主叫方只对前十行感兴趣,

 List<String> firstTen = Files.lines(path).limit(10).collect(toList()); 

当然,如果调用者过滤stream只返回符合模式的行,那么可以节省大量的内存空间。

似乎正在出现的一个习惯用法是在没有get前缀的情况下,将它所代表或包含的事物的名称的复数名称命名为stream返回方法。 此外,虽然stream()是一个合理的stream返回方法的名称,但只有一组可能的返回值时,有时会出现具有多个types值的聚合的类。 例如,假设您有一些包含属性和元素的对象。 您可能会提供两个stream返回API:

 Stream<Attribute> attributes(); Stream<Element> elements(); 

stream被devise为始终被“终止”在他们创build的相同expression式内?

大多数例子都是这样使用的。

注意:返回一个Stream与返回一个Iterator没有什么不同(被承认有更多的expression能力)

恕我直言,最好的解决scheme是封装为什么你这样做,而不是返回集合。

例如

 public int playerCount(); public Player player(int n); 

或者如果你打算数它们

 public int countPlayersWho(Predicate<? super Player> test); 

我认为这取决于你的情况。 可能是,如果你让你的Team实现了Iterable<Player> ,那就足够了。

 for (Player player : team) { System.out.println(player); } 

或在一个function风格:

 team.forEach(System.out::println); 

但是如果你想要一个更完整和stream畅的api,一个stream可能是一个很好的解决scheme。

也许Stream工厂会是更好的select。 只通过Stream展示集合的巨大胜利在于它能够更好地封装您的领域模型的数据结构。 任何使用你的域类都不可能仅仅通过暴露stream来影响你的List或者Set的内部工作。

它还鼓励您的域类的用户以更现代的Java 8风格编写代码。 通过保留现有的getter并添加新的Stream返回的getter,可以逐渐重构这种风格。 随着时间的推移,您可以重写您的遗留代码,直到您最终删除了所有返回List或Set的getter。 一旦你清除了所有的遗留代码,这种重构感觉非常好!

我可能会有2个方法,一个返回一个Collection ,一个返回集合作为一个Stream

 class Team { private List<Player> players = new ArrayList<>(); // ... public List<Player> getPlayers() { return Collections.unmodifiableList(players); } public Stream<Player> getPlayerStream() { return players.stream(); } } 

这是两全其美的。 客户可以select他们是否想要列表或stream,他们不必做额外的对象创build列表的不可变副本只是为了得到一个stream。

这也只增加了1个方法到你的API,所以你没有太多的方法