我刚刚开始玩Java 8和Lambda Expression,我很好奇我是否可以通过返回特定值来阻止Lambda中的Stream生成 (如null)。这是否可以使用Stream.generate()?
private int counter;
private void generate()
{
System.out.println(Stream.generate(() -> {
if (counter < 10) {
counter++;
return RandomUtils.nextInt(100);
} else {
return null;
}
}).count());
}
不幸的是,此代码不会终止,因此只需返回null
即可退出流。
答案 0 :(得分:24)
更新(2017年):
Java 9将包含这种新方法:
Stream<T> takeWhile(Predicate<? super T> predicate);
按条件限制流。因此,不再需要下面的解决方法了。
<强> ORIGINAL:强>
使用 Stream.generate ,这可以从lambda闭包中定义不。根据定义,它是无止境的。使用limit()
,您可以修改流的大小。但这对以下条件无济于事:
if random>10 then stop
有可能通过条件限制潜在的无休止的流。如果不知道尺寸,这是有用的。您的朋友是 Spliterator ,您的示例代码如下:
System.out.println( StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<Integer>() {
int counter = 0;
@Override
public boolean hasNext() {
return counter < 10;
}
@Override
public Integer next() {
counter++;
return RandomUtils.nextInt(100);
}
}, Spliterator.IMMUTABLE), false).count());
基本上,您可以从 Iterator 构建 Stream 。我正在使用这种结构,例如来自Stax XML的 XMLEvents 流 - 解析。
我知道这不是由lambda构造完成的,但IHMO解决了这个缺乏的功能,即按条件停止生成流项目。
我会非常感兴趣,如果有更好的方法来实现这一点(我的意思是这个流构造而不是XML处理;))或者如果以这种方式使用流存在根本缺陷。
答案 1 :(得分:6)
对于Lamdas来说这是不可能的,你无法控制表达式内部的流量。 甚至API文档都说Stream.generate会生成无限的流。
但是,您可以使用limit()方法限制Stream并实现所需的功能:
System.out.println(Stream.generate(() -> RandomUtils.nextInt(100)).limit(10).count());
答案 2 :(得分:1)
// If you are not looking for parallelism, you can use following method:
public static <T> Stream<T> breakStream(Stream<T> stream, Predicate<T> terminate) {
final Iterator<T> original = stream.iterator();
Iterable<T> iter = () -> new Iterator<T>() {
T t;
boolean hasValue = false;
@Override
public boolean hasNext() {
if (!original.hasNext()) {
return false;
}
t = original.next();
hasValue = true;
if (terminate.test(t)) {
return false;
}
return true;
}
@Override
public T next() {
if (hasValue) {
hasValue = false;
return t;
}
return t;
}
};
return StreamSupport.stream(iter.spliterator(), false);
}
答案 3 :(得分:0)
使用StreamSupport.stream(Spliterator, boolean)
请参见Spliterator上的JavaDoc。
这是示例分隔符:
public class GeneratingSpliterator<T> implements Spliterator<T>
{
private Supplier<T> supplier;
private Predicate<T> predicate;
public GeneratingSpliterator(final Supplier<T> newSupplier, final Predicate<T> newPredicate)
{
supplier = newSupplier;
predicate = newPredicate;
}
@Override
public int characteristics()
{
return 0;
}
@Override
public long estimateSize()
{
return Long.MAX_VALUE;
}
@Override
public boolean tryAdvance(final Consumer<? super T> action)
{
T newObject = supplier.get();
boolean ret = predicate.test(newObject);
if(ret) action.accept(newObject);
return ret;
}
@Override
public Spliterator<T> trySplit()
{
return null;
}
}
答案 4 :(得分:-1)
是可能的,你只需要在框外思考。
以下想法借鉴了Python,这种语言将我引入了生成器函数......
在RuntimeException
闭包内完成后,只需抛出Supplier<T>
的实例,并在呼叫站点捕捉并忽略它。
一个示例摘录(注意我添加了一个Stream.limit(Long.MAX_VALUE)
的安全捕获来覆盖意外情况,但它永远不会被触发):
static <T> Stream<T> read(String path, FieldSetMapper<T> fieldSetMapper) throws IOException {
ClassPathResource resource = new ClassPathResource(path);
DefaultLineMapper<T> lineMapper = new DefaultLineMapper<>();
lineMapper.setFieldSetMapper(fieldSetMapper);
lineMapper.setLineTokenizer(getTokenizer(resource));
return Stream.generate(new Supplier<T>() {
FlatFileItemReader<T> itemReader = new FlatFileItemReader<>();
int line = 1;
{
itemReader.setResource(resource);
itemReader.setLineMapper(lineMapper);
itemReader.setRecordSeparatorPolicy(new DefaultRecordSeparatorPolicy());
itemReader.setLinesToSkip(1);
itemReader.open(new ExecutionContext());
}
@Override
public T get() {
T item = null;
++line;
try {
item = itemReader.read();
if (item == null) {
throw new StopIterationException();
}
} catch (StopIterationException ex) {
throw ex;
} catch (Exception ex) {
LOG.log(WARNING, ex,
() -> format("%s reading line %d of %s", ex.getClass().getSimpleName(), line, resource));
}
return item;
}
}).limit(Long.MAX_VALUE).filter(Objects::nonNull);
}
static class StopIterationException extends RuntimeException {}
public void init() {
if (repository.count() == 0) {
Level logLevel = INFO;
try {
read("providers.csv", fields -> new Provider(
fields.readString("code"),
fields.readString("name"),
LocalDate.parse(fields.readString("effectiveStart"), DateTimeFormatter.ISO_LOCAL_DATE),
LocalDate.parse(fields.readString("effectiveEnd"), DateTimeFormatter.ISO_LOCAL_DATE)
)).forEach(repository::save);
} catch (IOException e) {
logLevel = WARNING;
LOG.log(logLevel, "Initialization was interrupted");
} catch (StopIterationException ignored) {}
LOG.log(logLevel, "{} providers imported.", repository.count());
}
}
答案 5 :(得分:-3)
我的解决方案是在完成后生成null,然后应用过滤器
Stream
.generate( o -> newObject() )
.filter( o -> o != null )
.forEach(...)