fre*_*low 152 java collections encapsulation java-8 java-stream
假设我有一个方法将只读视图返回到成员列表:
class Team {
private List < Player > players = new ArrayList < > ();
// ...
public List < Player > getPlayers() {
return Collections.unmodifiableList(players);
}
}
Run Code Online (Sandbox Code Playgroud)
进一步假设所有客户端都立即迭代一次列表.也许将玩家放入JList或其他东西.客户端就不能存储到列表的引用以便稍后进行检查!
鉴于这种常见情况,我应该返回一个流吗?
public Stream < Player > getPlayers() {
return players.stream();
}
Run Code Online (Sandbox Code Playgroud)
或者在Java中返回非惯用的流?设计的流是否始终在它们创建的同一表达式中"终止"?
Bri*_*etz 207
答案是,一如既往,"它取决于".这取决于返回的集合有多大.这取决于结果是否随时间变化,以及返回结果的一致性有多重要.这在很大程度上取决于用户如何使用答案.
首先,请注意您始终可以从Stream获取集合,反之亦然:
// If API returns Collection, convert with stream()
getFoo().stream()...
// If API returns Stream, use collect()
Collection<T> c = getFooStream().collect(toList());
Run Code Online (Sandbox Code Playgroud)
所以问题是,这对你的来电者更有用.
如果您的结果可能是无限的,那么只有一个选择:Stream.
如果你的结果可能非常大,你可能更喜欢Stream,因为一次实现它可能没有任何价值,这样做会造成很大的堆压力.
如果所有调用者都要迭代它(搜索,过滤,聚合),你应该更喜欢Stream,因为Stream已经内置了这些内置并且不需要实现集合(特别是如果用户可能不处理整个结果.)这是一个非常常见的情况.
即使您知道用户将多次迭代它或以其他方式保留它,您仍然可能想要返回一个Stream,因为简单的事实是您选择将其放入的任何Collection(例如,ArrayList)可能不是他们想要的形式,然后调用者无论如何都要复制它.如果你返回一个流,他们可以做到collect(toCollection(factory))并以他们想要的形式获得它.
上述"偏好流"案例主要源于Stream更灵活的事实; 您可以延迟绑定到如何使用它,而不会产生将其具体化为集合的成本和约束.
必须返回集合的一种情况是,当存在强一致性要求时,您必须生成移动目标的一致快照.然后,您将希望将元素放入不会更改的集合中.
所以,我要说的是,大部分的时间,流是正确的答案 - 这是更灵活,它不会强加通常,不必要的物化成本,如果需要,可以很容易地变成您所选择的集合.但有时,你可能必须返回一个集合(比如,由于强一致性要求),或者您可能需要返回集合,因为你知道用户将如何使用它,并且知道这对他们来说是最方便的事情.
Stu*_*rks 63
我有几点要补充Brian Goetz的优秀答案.
从"getter"样式方法调用返回Stream是很常见的.请参阅Java 8 javadoc中的Stream usage页面,并查找除了之外的软件包的"返回Stream的方法" java.util.Stream.这些方法通常位于表示或可包含多个值或某些聚合的类上.在这种情况下,API通常会返回集合或它们的数组.由于Brian在他的回答中提到的所有原因,在这里添加流返回方法非常灵活.其中许多类已经具有集合或数组返回方法,因为这些类早于Streams API.如果您正在设计新的API,并且提供流返回方法是有意义的,则可能没有必要添加收集返回方法.
Brian提到了将值"物化"到集合中的成本.为了放大这一点,这里实际上有两个成本:在集合中存储值的成本(内存分配和复制)以及首先创建值的成本.通过利用Stream的懒惰行为,通常可以减少或避免后者的成本.一个很好的例子是API java.nio.file.Files:
static Stream<String> lines(path)
static List<String> readAllLines(path)
Run Code Online (Sandbox Code Playgroud)
不仅readAllLines必须将整个文件内容保存在内存中才能将其存储到结果列表中,它还必须在返回列表之前将文件读取到最后.该lines方法在执行某些设置后几乎可以立即返回,将文件读取和换行保留到以后必要时 - 或者根本不行.这是一个巨大的好处,例如,如果调用者只对前十行感兴趣:
try (Stream<String> lines = Files.lines(path)) {
List<String> firstTen = lines.limit(10).collect(toList());
}
Run Code Online (Sandbox Code Playgroud)
当然,如果调用者过滤流以仅返回与模式匹配的行等,则可以节省大量的存储空间.
似乎正在出现的一个习惯用法是在它表示或包含的事物的复数名称之后命名流返回方法,而没有get前缀.此外,虽然stream()只有一组可能返回的值时,while 是流返回方法的合理名称,但有时会有类具有多种类型值的聚合.例如,假设您有一些包含属性和元素的对象.您可以提供两个流返回API:
Stream<Attribute> attributes();
Stream<Element> elements();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
34336 次 |
| 最近记录: |