Java 为什么 Iterable<T> 不提供 stream() 和 parallelStream() 方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23114015/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does Iterable<T> not provide stream() and parallelStream() methods?
提问by skiwi
I am wondering why the Iterable
interface does not provide the stream()
and parallelStream()
methods. Consider the following class:
我想知道为什么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();
}
}
It is an implementation of a Handas you can have cards in your hand while playing a Trading Card Game.
它是Hand 的一种实现,因为您可以在玩集换式纸牌游戏时手里拿着纸牌。
Essentially it wraps a List<Card>
, ensures a maximum capacity and offers some other useful features. It is better as implementing it directly as a List<Card>
.
本质上,它包装了 a List<Card>
,确保了最大容量并提供了一些其他有用的功能。最好将它直接作为List<Card>
.
Now, for convienience I thought it would be nice to implement Iterable<Card>
, such that you can use enhanced for-loops if you want to loop over it. (My Hand
class also provides a get(int index)
method, hence the Iterable<Card>
is justified in my opinion.)
现在,为了方便起见,我认为实现它会很好Iterable<Card>
,这样如果你想循环它,你可以使用增强的 for 循环。(我的Hand
班级也提供了一种get(int index)
方法,因此Iterable<Card>
我认为这是合理的。)
The Iterable
interface provides the following (left out javadoc):
该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);
}
}
Now can you obtain a stream with:
现在你可以获得一个流:
Stream<Hand> stream = StreamSupport.stream(hand.spliterator(), false);
So onto the real question:
所以进入真正的问题:
- Why does
Iterable<T>
not provide a default methods that implementstream()
andparallelStream()
, I see nothing that would make this impossible or unwanted?
- 为什么不
Iterable<T>
提供实现stream()
and的默认方法parallelStream()
,我看不到什么会使这成为不可能或不需要的?
A related question I found is the following though: Why does Stream<T> not implement Iterable<T>?
Which is oddly enough suggesting it to do it somewhat the other way around.
我发现的一个相关问题如下:为什么 Stream<T> 不实现 Iterable<T>?
奇怪的是,建议它以相反的方式来做。
采纳答案by Brian Goetz
This was not an omission; there was detailed discussion on the EG list in June of 2013.
这不是遗漏。2013年6月对EG名单进行了详细讨论。
The definitive discussion of the Expert Group is rooted at this thread.
专家组的最终讨论源于此线程。
While it seemed "obvious" (even to the Expert Group, initially) that stream()
seemed to make sense on Iterable
, the fact that Iterable
was so general became a problem, because the obvious signature:
虽然看起来“显而易见”(即使对于专家组,最初也是如此)stream()
似乎有意义Iterable
,但Iterable
如此普遍的事实却成为一个问题,因为明显的签名:
Stream<T> stream()
was not always what you were going to want. Some things that were Iterable<Integer>
would rather have their stream method return an IntStream
, for example. But putting the stream()
method this high up in the hierarchy would make that impossible. So instead, we made it really easy to make a Stream
from an Iterable
, by providing a spliterator()
method. The implementation of stream()
in Collection
is just:
并不总是你想要的。例如,有些东西Iterable<Integer>
宁愿让他们的流方法返回一个IntStream
。但是将stream()
方法放在层次结构中的如此高的位置会使这变得不可能。因此,相反,我们通过提供一种方法使Stream
从 an 生成a 变得非常容易。in的实现只是:Iterable
spliterator()
stream()
Collection
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
Any client can get the stream they want from an Iterable
with:
任何客户端都可以通过以下方式获取他们想要的流Iterable
:
Stream s = StreamSupport.stream(iter.spliterator(), false);
In the end we concluded that adding stream()
to Iterable
would be a mistake.
最后我们得出结论,添加stream()
到Iterable
将是一个错误。
回答by Edwin Dalorzo
I did an investigation in several of the project lambda mailing lists and I think I found a few interesting discussions.
我在几个项目 lambda 邮件列表中进行了调查,我想我发现了一些有趣的讨论。
I have not found a satisfactory explanation so far. After reading all this I concluded it was just an omission. But you can see here that it was discussed several times over the years during the design of the API.
到目前为止,我还没有找到令人满意的解释。阅读完所有这些后,我得出结论,这只是一个遗漏。但是您可以在这里看到,在 API 的设计过程中,多年来对其进行了多次讨论。
Lambda Libs Spec Experts
Lambda Libs 规范专家
I found a discussion about this in the Lambda Libs Spec Experts mailing list:
我在Lambda Libs Spec Experts 邮件列表中找到了关于此的讨论:
Under Iterable/Iterator.stream()Sam Pullara said:
在Iterable/Iterator.stream() 下Sam Pullara 说:
I was working with Brian on seeing how limit/substream functionality[1] might be implemented and he suggested conversion to Iterator was the right way to go about it. I had thought about that solution but didn't find any obvious way to take an iterator and turn it into a stream. It turns out it is in there, you just need to first convert the iterator to a spliterator and then convert the spliterator to a stream. So this brings me to revisit the whether we should have these hanging off one of Iterable/Iterator directly or both.
My suggestion is to at least have it on Iterator so you can move cleanly between the two worlds and it would also be easily discoverable rather than having to do:
Streams.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED))
我正在与 Brian 一起研究如何实现限制/子流功能 [1],他建议转换为 Iterator 是正确的方法。我曾考虑过该解决方案,但没有找到任何明显的方法来获取迭代器并将其转换为流。事实证明它就在那里,您只需要首先将迭代器转换为拆分器,然后将拆分器转换为流。所以这让我重新审视我们是否应该让这些直接挂在 Iterable/Iterator 之一上,或者两者都挂起来。
我的建议是至少将它放在 Iterator 上,这样你就可以在两个世界之间干净利落地移动,而且它也很容易被发现,而不必这样做:
Streams.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED))
And then Brian Goetz responded:
I think Sam's point was that there are plenty of library classes that give you an Iterator but don't let you necessarily write your own spliterator. So all you can do is call stream(spliteratorUnknownSize(iterator)). Sam is suggesting that we define Iterator.stream() to do that for you.
I would like to keep the stream() and spliterator() methods as being for library writers / advanced users.
我认为 Sam 的观点是,有很多库类可以为您提供迭代器,但不一定让您编写自己的拆分器。所以你所能做的就是调用stream(spliteratorUnknownSize(iterator))。Sam 建议我们定义 Iterator.stream() 来为你做这件事。
我想将 stream() 和 spliterator() 方法保留为图书馆作者/高级用户。
"Given that writing a Spliterator is easier than writing an Iterator, I would prefer to just write a Spliterator instead of an Iterator (Iterator is so 90s :)"
You're missing the point, though. There are zillions of classes out there that alreadyhand you an Iterator. And many of them are not spliterator-ready.
“鉴于编写 Spliterator 比编写 Iterator 更容易,我宁愿只编写 Spliterator 而不是 Iterator(Iterator 太 90 年代了:)”
不过,你没有抓住重点。有无数的类已经为您提供了一个迭代器。他们中的许多人还没有准备好拆分器。
Previous Discussions in Lambda Mailing List
Lambda 邮件列表中的先前讨论
This may not be the answer you are looking for but in the Project Lambda mailing listthis was briefly discussed. Perhaps this helps to foster a broader discussion on the subject.
这可能不是您正在寻找的答案,但在Project Lambda 邮件列表中对此进行了简要讨论。也许这有助于促进对该主题的更广泛讨论。
In the words of Brian Goetz under Streams from Iterable:
用来自 Iterable 的 Streams下的 Brian Goetz 的话:
Stepping back...
There are lots of ways to create a Stream. The more information you have about how to describe the elements, the more functionality and performance the streams library can give you. In order of least to most information, they are:
Iterator
Iterator + size
Spliterator
Spliterator that knows its size
Spliterator that knows its size, and further knows that all sub-splits know their size.
(Some may be surprised to find that we can extract parallelism even from a dumb iterator in cases where Q (work per element) is nontrivial.)
If Iterable had a stream() method, it would just wrap an Iterator with a Spliterator, with no size information. But, most things that are Iterable dohave size information. Which means we're serving up deficient streams. That's not so good.
One downside of the API practice outlined by Stephen here, of accepting Iterable instead of Collection, is that you are forcing things through a "small pipe" and therefore discarding size information when it might be useful. That's fine if all you're doing to do is forEach it, but if you want to do more, its better if you can preserve all the information you want.
The default provided by Iterable would be a crappy one indeed -- it would discard size even though the vast majority of Iterables do know that information.
退一步...
有很多方法可以创建流。关于如何描述元素的信息越多,流库可以为您提供的功能和性能就越多。从最少到最多的信息,它们是:
迭代器
迭代器 + 大小
分离器
知道其大小的 Spliterator
Spliterator 知道它的大小,并进一步知道所有子拆分都知道它们的大小。
(有些人可能会惊讶地发现,在 Q(每个元素的工作量)不平凡的情况下,我们甚至可以从愚蠢的迭代器中提取并行性。)
如果 Iterable 有一个 stream() 方法,它只会用 Spliterator 包装一个 Iterator,没有大小信息。但是,大部分的东西都是可迭代做有尺寸信息。这意味着我们正在提供有缺陷的流。那不太好。
Stephen 在这里概述的 API 实践的一个缺点,即接受 Iterable 而不是 Collection,是您强迫事物通过“小管道”,因此在可能有用时丢弃大小信息。如果您要做的只是 forEach it,那很好,但是如果您想做更多,最好能保留您想要的所有信息。
Iterable 提供的默认值确实很糟糕——即使绝大多数 Iterable 确实知道该信息,它也会丢弃大小。
Contradiction?
矛盾?
Although, it looks like the discussion is based on the changes that the Expert Group did to the initial design of Streams which was initially based on iterators.
虽然,看起来讨论是基于专家组对最初基于迭代器的 Streams 的初始设计所做的更改。
Even so, it is interesting to notice that in a interface like Collection, the stream method is defined as:
尽管如此,有趣的是注意到在像 Collection 这样的接口中,流方法定义为:
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
Which could be the exact the same code being used in the Iterable interface.
这可能与 Iterable 接口中使用的代码完全相同。
So, this is why I said this answer is probably not satisfactory, but still interesting for the discussion.
所以,这就是为什么我说这个答案可能并不令人满意,但对于讨论来说仍然很有趣。
Evidence of Refactoring
重构的证据
Continuing with the analysis in the mailing list, it looks like the splitIterator method was originally in the Collection interface, and at some point in 2013 they moved it up to Iterable.
继续邮件列表中的分析,看起来 splitIterator 方法最初在 Collection 接口中,并且在 2013 年的某个时候他们将其移至 Iterable。
Pull splitIterator up from Collection to Iterable.
将 splitIterator 从 Collection 拉到 Iterable。
Conclusion/Theories?
结论/理论?
Then chances are that the lack of the method in Iterable is just an omission, since it looks like they should have moved the stream method as well when they moved the splitIterator up from Collection to Iterable.
那么有可能是 Iterable 中缺少方法只是一个遗漏,因为当他们将 splitIterator 从 Collection 向上移动到 Iterable 时,看起来他们应该也移动流方法。
If there are other reasons those are not evident. Somebody else has other theories?
如果有其他原因,则不明显。其他人有其他理论吗?
回答by Udo
If you know the size you could use java.util.Collection
which provides the stream()
method:
如果您知道可以使用的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();
}
}
And then:
进而:
new Hand().stream().map(...)
I faced the same problem and was surprised that my Iterable
implementation could be very easily extended to an AbstractCollection
implementation by simply adding the size()
method (luckily I had the size of the collection :-)
我遇到了同样的问题,并且很惊讶我的Iterable
实现可以AbstractCollection
通过简单地添加size()
方法很容易地扩展到一个实现(幸运的是我有集合的大小:-)
You should also consider to override Spliterator<E> spliterator()
.
您还应该考虑覆盖Spliterator<E> spliterator()
.