注意:我不一定要寻找下面描述的具体示例问题的解决方案。我真的很感兴趣,为什么这在java 8中是不可能的。
Java流是懒惰的。最后,他们有一个单一的终端操作<我的解释是,这个终端操作将通过流提取所有值。没有任何中间操作可以做到这一点。为什么没有中间操作通过流拉入任意数量的元素?类似这样:
stream
.mapMultiple(this::consumeMultipleElements) // or groupAndMap or combine or intermediateCollect or reverseFlatMap
.collect(Collectors.toList());
当下游操作尝试推进流一次时,中间操作可能尝试多次(或根本不推进)上游。
我会看到几个用例:
(这些只是示例。所以您可以看到处理这些用例当然是可能的,但它“不是流式方式”,并且这些解决方案缺乏Streams所具有的理想的惰性属性。)
>
将多个元素组合成一个新元素,然后传递到流的其余部分。(例如,配对<代码>(1,2,3,4,5,6)➔ ((1,2)、(3,4)、(5,6)))
// Something like this,
// without needing to consume the entire stream upfront,
// and also more generic. (The combiner should decide for itself how many elements to consume/combine per resulting element. Maybe the combiner is a Consumer<Iterator<E>> or a Consumer<Supplier<E>>)
public <E, R> Stream<R> combine(Stream<E> stream, BiFunction<E, E, R> combiner) {
List<E> completeList = stream.collect(toList());
return IntStream.range(0, completeList.size() / 2)
.mapToObj(i -> combiner.apply(
completeList.get(2 * i),
completeList.get(2 * i + 1)));
}
确定流是否为空(将流映射到可选的非空流)
// Something like this, without needing to consume the entire stream
public <E> Optional<Stream<E>> toNonEmptyStream(Stream<E> stream) {
List<E> elements = stream.collect(toList());
return elements.isEmpty()
? Optional.empty()
: Optional.of(elements.stream());
}
具有不终止流的惰性迭代器(允许基于更复杂的逻辑跳过元素,然后只跳过(长n)。
Iterator<E> iterator = stream.iterator();
// Allow this without throwing a "java.lang.IllegalStateException: stream has already been operated upon or closed"
stream.collect(toList());
当他们设计流及其周围的一切时,他们是忘记了这些用例还是明确地忽略了这一点<我理解,在处理并行流时,这些可能会产生意外的结果,但在我看来,这是一种可以记录的风险。
并非每个终端操作都会“通过流提取所有值”。终端操作iterator()
和spliterator()
不会立即获取所有值并允许执行延迟处理,包括再次构建新的Stream
。对于后者,强烈建议使用spliterator()
,因为这允许将更多元信息传递给新流,也意味着减少对象的包装。
E、 g.您的第二个示例可以实现为
public static <T> Stream<T> replaceWhenEmpty(Stream<T> s, Supplier<Stream<T>> fallBack) {
boolean parallel = s.isParallel();
Spliterator<T> sp = s.spliterator();
Stream.Builder<T> firstElement;
if(sp.getExactSizeIfKnown()==0 || !sp.tryAdvance(firstElement=Stream.builder())) {
s.close();
return fallBack.get();
}
return Stream.concat(firstElement.build(), StreamSupport.stream(sp, parallel))
.onClose(s::close);
}
对于你的一般性问题,我不知道这些示例的一般抽象应该是什么样子,除了已经存在的spliterator()
方法。正如文件所说
然而,如果提供的流操作不提供所需的功能,则基流。迭代器()和BaseStream。spliterator()操作可用于执行受控遍历。
您想要的所有操作实际上都可以在流API中实现,但不是现成的。
将多个元素组合成一对元素-您需要一个自定义的拆分器。塔吉尔·瓦列夫正在做这件事。他拥有一个名为StreamEx的绝对强大的库,它可以做许多其他现成不支持的有用的事情。
我不理解你的第二个例子,但我打赌它也是可行的。
跳过
到更复杂的操作是在java-9
中通过以谓词
为输入的dropAnd
和takANG
。
请注意,当您说中间操作都不能做到这一点时,这是不准确的-有排序的和不同的。否则他们无法工作。还有类似的flatMap,但这更像是一个bug。
还有一件事是,并行流的中间操作没有定义的顺序,因此这种有状态的中间操作将具有并行流的未知项。另一方面,你总是可以选择滥用以下内容:
List<Something> list = Collections.synchronizedList()
.map(x -> {
list.add(x);
// your mapping
})
如果我是你,我不会那么做,如果我真的需要的话,但以防万一。。。
问题内容: 我知道每次键入字符串文字时,字符串池中都会引用相同的String对象。 但是,为什么String API不包含,所以我可以使用引用? 至少,这将节省编译时间,因为编译器将知道引用现有的String,而不必检查是否已创建它以进行重用,对吗?我个人认为,字符串文字(尤其是很小的文字)在许多情况下是一种“代码异味”。 那么是否没有String.Empty背后的宏伟设计原因,还是语言创建者根本
问题内容: 在Java中,有和接口。两者都属于Java的标准框架,并提供了一种访问元素的分类方法。 但是,据我了解没有。你可以用来对列表进行排序。 知道为什么要这样设计吗? 问题答案: 列表迭代器首先确保你以列表的内部顺序(也称为插入顺序)获取列表的元素。更具体地说,它是按照插入元素的顺序或操作列表的方式进行的。排序可以看作是对数据结构的一种操作,有几种方法可以对列表进行排序。 我将按照自己的见解
我运行了一个简单的wordcount MapReduce示例,在组合器输出中添加一个小变化的组合器,组合器的输出不被Reducer合并。场景如下 context.write(t,new IntWritable(1));//添加了我自己的输出 我运行了一个简单的wordcount MapReduce示例,在组合器输出中添加一个小变化的组合器,组合器的输出不被Reducer合并。场景如下:在组合器中,
问题内容: 恐怕这是一个愚蠢的问题。 有谁能告诉我为什么没有对立的东西? 除了“因为根本就没有”以外,还有其他原因吗? 我应该创建自己的一个吗?还是我想念其他东西? 更新资料 在哪里使用?我正在编写一个使用大量消费者和供应商的图书馆。我成功地写了一行,并且遇到一种情况,期望消费者接受来自方法结果的布尔值。说什么 问题答案: 并且需要避免开销自动装箱每个值。处理原始图元更有效。但是,对于布尔值和字节
问题内容: 在Java has 方法中,但是,它仅在诸如或的关联容器中使用。为什么要这样设计?具有方法的界面看起来更优雅。 问题答案: 在我看来,主要的论据是,可以为任何Java对象计算出一个定义明确的默认值,以及一个同样定义明确的。根本没有充分的理由要保留所有对象的该功能,当然也有很多理由 不 保留此功能。因此,这本书毫无疑问。
问题内容: 在Java中,为什么以下代码行不起作用? 如果我将其更改为 起初,我以为您可能没有接口列表,但是我可以创建一个很好的接口。 有想法吗? 问题答案: 泛型类型比较古怪。 表示或任何子类型,但仅表示。如果您想要一个子类型,您需要 我怀疑你可以用 无法执行此操作的原因是,您可以使用对引用的引用,并且必须谨慎使用额外的间接级别。 使用泛型,您可以有两个间接级别,这会给您带来问题,因此它们更容易