与C#不同IEnumerable,执行管道可以根据需要执行多次,在Java中,流只能"迭代"一次.
对终端操作的任何调用都会关闭流,使其无法使用.这个"功能"消耗了很多力量.
我想这个的原因不是技术性的.这个奇怪限制背后的设计考虑是什么?
编辑:为了演示我在说什么,请考虑以下C#中的Quick-Sort实现:
IEnumerable<int> QuickSort(IEnumerable<int> ints)
{
if (!ints.Any()) {
return Enumerable.Empty<int>();
}
int pivot = ints.First();
IEnumerable<int> lt = ints.Where(i => i < pivot);
IEnumerable<int> gt = ints.Where(i => i > pivot);
return QuickSort(lt).Concat(new int[] { pivot }).Concat(QuickSort(gt));
}
Run Code Online (Sandbox Code Playgroud)
现在可以肯定的是,我并不是说这是一个很好的快速排序!然而,它是lambda表达式与流操作相结合的表达能力的一个很好的例子.
它不能用Java完成!我甚至无法询问流是否为空而不使其无法使用.
我正在学习新的Java 8功能,在尝试使用streams(java.util.stream.Stream)和收集器时,我意识到流不能使用两次.
有没有办法重复使用它?
我有一个方法,在流上执行处理.部分处理需要在一个锁的控制下完成 - 一个锁定部分用于处理所有元素 - 但其中一部分没有(并且不应该因为它可能非常耗时).所以我不能只说:
Stream<V> preprocessed = Stream.of(objects).map(this::preProcess);
Stream<V> toPostProcess;
synchronized (lockObj) {
toPostProcess = preprocessed.map(this::doLockedProcessing);
}
toPostProcess.map(this::postProcess).forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
因为调用doLockedProcessing只会在forEach调用终端操作时执行,并且在锁定之外.
所以我认为我需要在每个阶段使用终端操作制作流的副本,以便在正确的时间完成正确的位.就像是:
Stream<V> preprocessed = Stream.of(objects).map(this::preProcess).copy();
Stream<V> toPostProcess;
synchronized (lockObj) {
toPostProcess = preprocessed.map(this::doLockedProcessing).copy();
}
toPostProcess.map(this::postProcess).forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
当然,该copy()方法不存在,但如果确实如此,它将对流执行终端操作并返回包含所有相同元素的新流.
我知道实现这一目标的几种方法:
(1)通过数组(如果元素类型是泛型类型,则不是那么容易):
copy = Stream.of(stream.toArray(String[]::new));
Run Code Online (Sandbox Code Playgroud)
(2)通过列表:
copy = stream.collect(Collectors.toList()).stream();
Run Code Online (Sandbox Code Playgroud)
(3)通过流构建器:
Stream.Builder<V> builder = Stream.builder();
stream.forEach(builder);
copy = builder.build();
Run Code Online (Sandbox Code Playgroud)
我想知道的是:这些方法中哪一种在时间和记忆方面最有效?或者还有另一种方式更好吗?
如何在 java8(可能是记忆过程)中重用已经通过流迭代计算出的值?
如果流被重复或再次提供,它将被重新计算。在某些情况下,最好用内存来换取 CPU 时间。从头开始收集所有内容可能不是一个好主意,因为流用于查找满足谓词的第一个项目。
Stream<Integer> all = Stream.of(1,2,3,4,5, ...<many other values>... ).
map(x->veryLongTimeToComputeFunction(x));
System.out.println("fast find of 2"+all.filter(x->x>1).findFirst());
//both of these two lines generate a "java.lang.IllegalStateException: stream has already been operated upon or closed"
System.out.println("no find"+all.filter(x->x>10).findFirst());
System.out.println("find again"+all.filter(x->x>4).findFirst());
Run Code Online (Sandbox Code Playgroud)
以下是我的代码:
void deleteTranslationIfUpdated(Stream<MediaTranslation> from, Stream<MediaTranslationDTO> data) {
Stream<MediaTranslation> newLogos = data
.map(mediaTranslationMapper::mapToEntity)
.collect(Collectors.toList())
.stream();
from.filter(e ->
newLogos.noneMatch(it ->
Objects.equals(e.getLang(), it.getLang()) && Objects.equals(e.getValue().getLink(), it.getValue().getLink())))
.map(MediaTranslation::getValue)
.map(Media::getLink)
.filter(this::isNotHttpLink)
.forEach(storageService::delete);
}
Run Code Online (Sandbox Code Playgroud)
以上函数从以下函数调用:
@Secured({AuthoritiesConstants.USER})
public BrandDTO update(String id, BrandDTO data) throws EntityNotFound {
log.debug("Request to update Brand : {} with {}", id, data);
return Optional.ofNullable(brandRepository.findOne(id))
.map(from -> {
mediaService.deleteTranslationIfUpdated(from.getLogo().stream(), data.getLogo().stream());
return from;
})
.map(target -> brandMapper.updateFromDto(data, target))
.map(brandRepository::save)
.map(brandMapper::mapToDto)
.map(this::copyCategoriesInZone)
.orElseThrow(EntityNotFound::new);
}
Run Code Online (Sandbox Code Playgroud)
每当我这样做,我得到以下错误:
java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229) …
为什么以下java 8代码在第二次调用get()时显示错误?
Stream<String> aStream = Stream.concat(Stream.of("A"), Stream.of("B"));
String a = stream.findFirst().get();
String b = stream.findFirst().get();
Run Code Online (Sandbox Code Playgroud)
"aStream"流应该看到两个值:"A"和"B".但是,在第一个元素已被消耗之后尝试读取任何内容
java.lang.IllegalStateException: stream has already been operated upon or closed
Run Code Online (Sandbox Code Playgroud)
这不是Java 8中的错误吗?首先,为什么不消耗的Stream.of()-created流返回Optional与isPresent()==false?第二,为什么不Stream.concatenate()正确连接这样的Stream.of()创建流?
java ×6
java-stream ×6
java-8 ×4
api-design ×1
jhipster ×1
lambda ×1
memoization ×1
reusability ×1