我想知道为什么Iterable
接口不提供stream()
和parallelStream()
方法。考虑以下课程:
public class Hand implements Iterable<Card> {
private final List<Card> list = new ArrayList<>();
private final int capacity;
//...
@Override
public Iterator<Card> iterator() {
return list.iterator();
}
}
这是 Hand 的一个实现,因为你可以在玩交易卡游戏时拿到牌。
基本上它包裹List<Card>
,确保最大容量并提供一些其他有用的功能。最好将其直接实现为List<Card>
。
现在,为了方便起见,我认为实现Iterable<Card>
会很好,这样如果你想循环它就可以使用增强的for循环。 (我的Hand
课程也提供了get(int index)
方法,因此我认为Iterable<Card>
是合理的。)
Iterable
接口提供以下内容(省略javadoc):
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
现在可以获得一个包含以下内容的流:
Stream<Hand> stream = StreamSupport.stream(hand.spliterator(), false);
关于真正的问题:
Iterable<T>
没有提供实施stream()
和parallelStream()
的默认方法,我认为什么都不会导致这种情况变得不可能或不需要?我发现的一个相关问题如下:Why does Stream<T> not implement Iterable<T>?
奇怪的是,这表明它在某种程度上是相反的。
答案 0 :(得分:276)
这不是遗漏; 2013年6月对EG名单进行了详细讨论。
专家组的最终讨论植根于this thread。
虽然看起来很明显&#34; (即使是最初的专家组)stream()
Iterable
似乎有意义,Iterable
如此普遍的事实成为一个问题,因为显而易见的签名:
Stream<T> stream()
并不总是你想要的。例如,Iterable<Integer>
的某些内容宁愿让其stream方法返回IntStream
。但是将stream()
方法放在层次结构中会使这个问题变得不可能。因此,通过提供Stream
方法,我们可以很容易地从Iterable
制作spliterator()
。 stream()
中Collection
的实施只是:
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
任何客户都可以从Iterable
获取他们想要的流:
Stream s = StreamSupport.stream(iter.spliterator(), false);
最后我们得出结论,将stream()
添加到Iterable
将是一个错误。
答案 1 :(得分:23)
我在几个项目lambda邮件列表中进行了调查,我想我发现了一些有趣的讨论。
到目前为止,我还没有找到令人满意的解释。阅读完所有这些后我得出的结论只是一个遗漏。但是你可以在这里看到,在设计API的过程中多年来已经多次讨论过这个问题。
Lambda Libs Spec Experts
我在Lambda Libs Spec Experts mailing list:
中找到了对此的讨论在Iterable/Iterator.stream()下,Sam Pullara说:
我正在和Brian一起看看如何限制/子流 功能[1]可能已实施,他建议转换为 迭代器是正确的方法。我考虑过这个 解决方案,但没有找到任何明显的方法来采取迭代器和转向 它变成了流。原来它在那里,你只需要先 将迭代器转换为spliterator然后转换spliterator 到一个流。所以这让我重新审视我们是否应该拥有 这些可以直接挂掉Iterable / Iterator中的一个或两者。
我的建议是至少在Iterator上有它,所以你可以移动 两个世界之间干净利落,也很容易 可发现而不是必须:
Streams.stream(Spliterators.spliteratorUnknownSize(迭代器, Spliterator.ORDERED))
我认为Sam的观点是有很多库类 给你一个迭代器,但不要让你自己写 spliterator。所以你能做的就是打电话 流(spliteratorUnknownSize(迭代))。山姆在暗示我们 定义Iterator.stream()为你做这件事。
我想将stream()和spliterator()方法保持为 对于图书馆作家/高级用户。
&#34;鉴于编写Spliterator比编写Iterator更容易, 我更愿意只写一个Spliterator而不是Iterator(Iterator是90s):&#34;
但是,你错过了这一点。有数以万计的课程 那里已经递给你一个迭代器。其中很多都不是 spliterator就绪
Lambda邮件列表中的先前讨论
这可能不是您正在寻找的答案,但在Project Lambda mailing list中对此进行了简要讨论。也许这有助于促进关于这一主题的更广泛的讨论。
用Streams from Iterable下的Brian Goetz的话说:
退后一步......
有很多方法可以创建Stream。你的信息越多 有关如何描述元素,功能和更多 流库可以给你带来的性能。按顺序至少 大多数信息,他们是:
迭代
Iterator + size
Spliterator
知道其大小的Spliterator
Spliterator知道它的大小,并进一步知道所有的子分裂 知道他们的大小。
(有些人可能会惊讶地发现我们甚至可以提取并行性 在Q(每个元素的工作量)的情况下,从一个愚蠢的迭代器 平凡的。)
如果Iterable有一个stream()方法,它只会包装一个Iterator Spliterator,没有大小信息。但是,大多数事情都是如此 Iterable do 具有大小信息。这意味着我们正在提供服务 流量不足。那不太好。
Stephen在这里概述的API实践的一个缺点 接受Iterable而不是Collection,就是你强迫 通过&#34;小管道#34;因此丢弃尺寸 可能有用的信息。如果您所有人都这样做,这很好 要做到的就是每一个,但如果你想做更多,如果你做得更好 可以保留您想要的所有信息。
Iterable提供的默认设置确实很糟糕 - 它 即使绝大多数Iterables都知道,也会丢弃大小 那个信息。
<强>矛盾吗
虽然看起来讨论是基于专家组对最初基于迭代器的Streams初始设计所做的更改。
即便如此,有趣的是注意到在像Collection这样的接口中,stream方法定义为:
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
这可能与Iterable接口中使用的代码完全相同。
所以,这就是为什么我说这个答案可能不太令人满意,但仍然对讨论感兴趣。
重构的证据
继续在邮件列表中进行分析,看起来splitIterator方法最初位于Collection界面中,并且在2013年的某个时候,它们将其移动到Iterable。
Pull splitIterator up from Collection to Iterable
<强>结论/理论吗
然后很可能Iterable中缺少方法只是一个遗漏,因为看起来他们应该在将splitIterator从Collection移动到Iterable时移动了stream方法。
如果还有其他原因,那些不明显。还有其他人有其他理论吗?
答案 2 :(得分:6)
如果您知道可以使用提供java.util.Collection
方法的stream()
的尺寸:
public class Hand extends AbstractCollection<Card> {
private final List<Card> list = new ArrayList<>();
private final int capacity;
//...
@Override
public Iterator<Card> iterator() {
return list.iterator();
}
@Override
public int size() {
return list.size();
}
}
然后:
new Hand().stream().map(...)
我遇到了同样的问题,并且很惊讶我的Iterable
实施可以通过简单地添加AbstractCollection
方法很容易地扩展到size()
实现(幸运的是我的大小与收集: - )
您还应该考虑覆盖Spliterator<E> spliterator()
。