为什么java.util.Collection没有实现新的Stream接口? [英] Why doesn't java.util.Collection implement the new Stream interface?

查看:113
本文介绍了为什么java.util.Collection没有实现新的Stream接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我花了一些时间开始研究关于流和lambdas的java-8嗡嗡声。让我感到意外的是,你不能直接应用Stream操作,如 .map() .filter()一个 java.util.Collection 。是否有一个技术原因,为什么 java.util.Collection 接口没有用这些Stream操作的
默认实现扩展?



使用Google搜索一下,我看到很多人沿着以下模式编码的例子:

  List< String> ; list = someListExpression; 
列表< String> anotherList = list.stream()。map(x - > f(x))。collect(Collectors.toList());

如果您的代码中有很多这样的流操作,它会变得非常笨拙。由于 .stream() .collect()与你想表达的内容完全无关,所以你更喜欢要说:

 列表< String> list = someListExpression; 
列表< String> anotherList = list.map(x - > f(x));


解决方案



关键是 lazy 操作之间的区别。您在第一个问题下给出的例子显示了在映射或过滤列表时产生新列表的渴望操作。这没什么不对,但往往不是你想要的,因为你经常在做比你需要的更多的工作;一个急切的操作必须对每个元素进行操作,并产生一个新的集合。如果你正在编写多个操作(filter-map-reduce),那么你正在做很多额外的工作。另一方面,懒惰的行动构成精美;如果您这样做:

 可选< Person> tallestGuy = people.stream()
.filter(p - > p.getGender()== MALE)
.max(比较(Person :: getHeight));

过滤器和减少(最大)操作融合在一起成为一次。这非常有效。

那么为什么不直接在List上暴露Stream方法呢?好吧,我们就这样试了。在许多其他原因中,我们发现混合像 filter()这样的惰性方法和像 removeAll()这样的热切方法混淆了给用户。通过将懒惰方法分组为一个单独的抽象,它变得更清晰; List 上的方法是那些改变列表的方法; Stream 上的方法是那些处理数据序列的可组合的惰性操作,无论数据在哪里存在。



所以,如果你想要做简单的事情,那么你认为这很棒,但当你尝试构建时,它会开始崩溃。额外的 stream()方法是否令人讨厌?当然。但是对数据结构(主要是关于在内存中组织数据)和流(主要是关于组合聚合行为)的抽象保持不变,这些抽象更好地分解为更复杂的操作。



对于第二个问题,您可以相对简单地执行此操作:实现像这样的流方法:

 公共< U>流< U> map(Function< T,U> mapper){return convertToStream()。map(mapper); } 

但这只是逆潮而游;最好只实现一个有效的stream()方法。


I just took some time to start looking into the java-8 buzz about streams and lambdas. What surprised me is that you cannot apply the Stream operations, like .map(), .filter() directly on a java.util.Collection. Is there a technical reason why the java.util.Collection interface was not extended with default implementations of these Stream operations?

Googling a bit, I see lots of examples of people coding along the pattern of:

List<String> list = someListExpression;
List<String> anotherList = list.stream().map(x -> f(x)).collect(Collectors.toList());

which becomes very clumsy, if you have a lot of these stream-operations in your code. Since .stream() and .collect() are completely irrelevant to what you want to express, you would rather like to say:

List<String> list = someListExpression;
List<String> anotherList = list.map(x -> f(x));

解决方案

Yes, there are excellent reasons for these decisions :)

The key is the difference between eager and lazy operations. The examples you give under the first question show eager operations where mapping or filtering a list produces a new list. There's nothing wrong with this, but it is often not what you want, because you're often doing way more work than you need; an eager operation must operate on every element, and produce a new collection. If you're composing multiple operations (filter-map-reduce), you're doing a lot of extra work. On the other hand, lazy operations compose beautifully; if you do:

 Optional<Person> tallestGuy = people.stream()
                                     .filter(p -> p.getGender() == MALE)
                                     .max(comparing(Person::getHeight));

the filter and reduce (max) operations are fused together into a single pass. This is very efficient.

So, why not expose the Stream methods right on List? Well, we tried it like that. Among numerous other reasons, we found that mixing lazy methods like filter() and eager methods like removeAll() was confusing to users. By grouping the lazy methods into a separate abstraction, it becomes much clearer; the methods on List are those that mutate the list; the methods on Stream are those that deal in composible, lazy operations on data sequences regardless of where that data lives.

So, the way you suggest it is great if you want to do really simple things, but starts to fall apart when you try to build on it. Is the extra stream() method annoying? Sure. But keeping the abstractions for data structures (which are largely about organizing data in memory) and streams (which are largely about composing aggregate behavior) separate scales better to more sophisticated operations.

To your second question, you can do this relatively easily: implement the stream methods like this:

public<U> Stream<U> map(Function<T,U> mapper) { return convertToStream().map(mapper); }

But that's just swimming against the tide; better to just implement an efficient stream() method.

这篇关于为什么java.util.Collection没有实现新的Stream接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆