Java 是否有任何方法可以停止从Lambda闭包生成的Stream.generate?
我刚刚开始研究Java8和Lambda表达式,我很好奇是否可以通过返回一个特定的值从Lambda Expression内部停止流生成 (如null)。这在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(
专用整数计数器;
私有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;
}
}