有没有办法阻止Stream.generate从它的Lambda闭包?

时间:2014-03-25 09:47:48

标签: java lambda java-8

我刚刚开始玩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即可退出流。

6 个答案:

答案 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(...)