我花了一些时间开始研究关于stream和lambdas的java-8嗡嗡声。让我感到惊讶的是,您无法直接在.map()
上应用Stream操作,例如.filter()
,java.util.Collection
。是否有技术原因导致java.util.Collection
接口没有扩展
这些Stream操作的默认实现?
谷歌搜索了一下,我看到很多人按照以下模式编码的例子:
List<String> list = someListExpression;
List<String> anotherList = list.stream().map(x -> f(x)).collect(Collectors.toList());
如果您的代码中有很多这些流操作,那么会变得非常笨拙。由于.stream()
和.collect()
与您想要表达的内容完全无关,因此您更愿意说:
List<String> list = someListExpression;
List<String> anotherList = list.map(x -> f(x));
答案 0 :(得分:85)
是的,这些决定有很好的理由:)
关键是渴望和懒惰操作之间的区别。您在第一个问题下给出的示例显示了急切的操作,其中映射或过滤列表会生成新列表。这没有什么不对,但它往往不是你想要的,因为你经常做的工作比你需要的多;渴望的操作必须对每个元素进行操作,并产生新的集合。如果你正在编写多个操作(filter-map-reduce),那么你需要做很多额外的工作。另一方面,懒惰的操作组合得很漂亮;如果你这样做:
Optional<Person> tallestGuy = people.stream()
.filter(p -> p.getGender() == MALE)
.max(comparing(Person::getHeight));
过滤器和reduce(max)操作融合在一起。这非常有效。
那么,为什么不直接在List上公开Stream方法呢?好吧,我们就这样试过了。在众多其他原因中,我们发现混合惰性方法(如filter()
)和急切方法(如removeAll()
)会让用户感到困惑。通过将惰性方法分组为单独的抽象,它变得更加清晰; List
上的方法是那些改变列表的方法; Stream
上的方法是那些处理数据序列的可合并,懒惰操作的方法,无论数据存在于何处。
所以,如果你想做一些非常简单的事情,你建议它的方式很好,但是当你试图在它上面构建时,它会开始分崩离析。额外的stream()
方法是否烦人?当然。但是,保持数据结构的抽象(主要是关于在内存中组织数据)和流(主要是关于组合聚合行为)将更好地扩展到更复杂的操作。
关于第二个问题,你可以相对容易地做到这一点:实现像这样的流方法:
public<U> Stream<U> map(Function<T,U> mapper) { return convertToStream().map(mapper); }
但那只是逆流而上;最好只实现一个有效的stream()方法。