Java 是否有任何方法可以停止从Lambda闭包生成的Stream.generate?

Java 是否有任何方法可以停止从Lambda闭包生成的Stream.generate?,java,lambda,java-8,Java,Lambda,Java 8,我刚刚开始研究Java8和Lambda表达式,我很好奇是否可以通过返回一个特定的值从Lambda Expression内部停止流生成 (如null)。这在Stream.generate()中可能吗 专用整数计数器; 私有void生成() { System.out.println(Stream.generate(()->{ 如果(计数器新建提供程序( fields.readString(“代码”), fields.readString(“名称”), parse(fields.readString(

我刚刚开始研究Java8和Lambda表达式,我很好奇是否可以通过返回一个特定的值从Lambda Expression内部停止流生成 (如null)。这在Stream.generate()中可能吗

专用整数计数器;
私有void生成()
{
System.out.println(Stream.generate(()->{
如果(计数器<10){
计数器++;
返回RandomUtils.nextInt(100);
}否则{
返回null;
}
}).count());
}

不幸的是,此代码没有终止,因此仅返回
null
不会跳出流。

这在Lamdas中是不可能的,您无法从表达式内部控制流。 甚至API文档也说Stream.generate生成无限流

但是,只需使用limit()方法即可限制流并实现所需的功能:

更新(2017):

Java 9将包括以下新方法:

Stream<T> takeWhile(Predicate<? super T> predicate); 
有可能根据条件限制潜在的无止境流。如果不知道尺寸,这是有用的。您的朋友是一个拆分器,您的示例代码如下所示:

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());
System.out.println(StreamSupport.stream(Spliterators.spliteratorUnknownSize)(新迭代器(){
int计数器=0;
@凌驾
公共布尔hasNext(){
返回计数器<10;
}
@凌驾
公共整数next(){
计数器++;
返回RandomUtils.nextInt(100);
}
},Spliterator.IMMUTABLE),false);
基本上,您可以从迭代器构建流。我正在使用这个构造,例如,用于来自Stax XML解析的XMLEvents

我知道这不是由lambda构造完成的,但它解决了IHMO缺少的按条件停止流项生成的功能


如果有更好的方法来实现这一点(我指的是这个流构造,而不是XML处理;),或者以这种方式使用流存在一个基本缺陷,我会非常感兴趣

我的解决方案是在完成时生成空值,然后应用过滤器

Stream
 .generate( o -> newObject() )
 .filter( o -> o != null )
 .forEach(...)
这是可能的,你只需要跳出框框思考

下面的想法是从Python中借用的,Python是一种向我介绍生成器函数的语言

当您在
供应商
关闭中完成时,只需抛出一个
RuntimeException
实例,然后在调用站点捕获并忽略它

一个示例摘录(注意,我添加了一个
Stream.limit(Long.MAX_VALUE)
的安全捕捉,以覆盖意外情况,尽管它不应该被触发):

静态流读取(字符串路径,FieldSetMapper FieldSetMapper)引发IOException{
ClassPathResource=new ClassPathResource(路径);
DefaultLineMapper lineMapper=新的DefaultLineMapper();
lineMapper.setFieldSetMapper(fieldSetMapper);
setLineTokenizer(getTokenizer(资源));
返回流.生成(新供应商(){
FlatFileItemReader itemReader=新FlatFileItemReader();
内线=1;
{
setResource(资源);
itemReader.setLineMapper(lineMapper);
setRecordSeparatorPolicy(新的DefaultRecordSeparatorPolicy());
itemReader.setLinesToSkip(1);
打开(新的ExecutionContext());
}
@凌驾
公共部门得不到{
T项=null;
++线路;
试一试{
item=itemReader.read();
如果(项==null){
抛出新的StopIterationException();
}
}捕获(StopIterationException-ex){
掷骰子;
}捕获(例外情况除外){
LOG.LOG(警告,例如,
()->格式(“%s读取第%d行,共%s行”,例如getClass().getSimpleName(),第行,资源));
}
退货项目;
}
}).limit(Long.MAX_值).filter(Objects::nonNull);
}
静态类StopIterationException扩展RuntimeException{}
公共void init(){
if(repository.count()==0){
级别logLevel=INFO;
试一试{
读取(“providers.csv”,字段->新建提供程序(
fields.readString(“代码”),
fields.readString(“名称”),
parse(fields.readString(“effectiveStart”)、DateTimeFormatter.ISO_LOCAL_DATE),
parse(fields.readString(“effectiveEnd”)、DateTimeFormatter.ISO\u LOCAL\u DATE)
)).forEach(存储库::保存);
}捕获(IOE异常){
日志级别=警告;
LOG.LOG(logLevel,“初始化被中断”);
}捕获(StopIterationException被忽略){}
LOG.LOG(logLevel,{}导入的提供者),repository.count();
}
}
//如果您不寻求并行性,可以使用以下方法:
公共静态流breakStream(流,谓词终止){
final Iterator original=stream.Iterator();
Iterable iter=()->新迭代器(){
T;
布尔值=false;
@凌驾
公共布尔值hasNext(){
如果(!original.hasNext()){
返回false;
} 
t=原始的。下一个();
hasValue=true;
if(terminate.test(t)){
返回false;
} 
返回true;
} 
@凌驾
公共T next(){
如果(hasValue){
hasValue=false;
返回t;
} 
返回t;
} 
};
返回StreamSupport.stream(iter.spliterator(),false);
}
使用
请参阅上的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;
    }
}
公共类生成拆分器实现拆分器
{
私人供应商;
私有谓词;
公共生成分配器(最终供应商新闻供应)
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());
Stream
 .generate( o -> newObject() )
 .filter( o -> o != null )
 .forEach(...)
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());
    }
}
// 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);
}
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;
    }
}