如果没有元素,则流的特殊行为

时间:2016-02-23 14:41:28

标签: java java-stream

我如何用java8 streaming-API表达这个?

  

我想为流的每个项目执行var casper = require('casper').create(); var fs = require('fs'); var data = fs.read('abc.txt'); casper.start(); casper.wait(5000, function () { console.log('wait 5000 for editing the file'); }); casper.then(function (){ console.log(data); }); casper.run(); 。如果有   不是我想要执行的项目itemConsumer

当然我可以这样写:

emptyAction

但我宁愿避免任何Consumer<Object> itemConsumer = System.out::println; Runnable emptyAction = () -> {System.out.println("no elements");}; Stream<Object> stream = Stream.of("a","b"); // or Stream.empty() List<Object> list = stream.collect(Collectors.toList()); if (list.isEmpty()) emptyAction.run(); else list.stream().forEach(itemConsumer);

我还想过在List方法中设置一个标志 - 但该标志是非final的,因此不允许。使用布尔容器似乎也是一种解决方法。

5 个答案:

答案 0 :(得分:3)

您可以强迫reduce执行此操作。如果遇到任何有用的数据,逻辑将是减少false,将值设置为true

reduce的结果是false,然后没有遇到任何项目。如果遇到任何项目,则结果为true

boolean hasItems = stream.reduce(false, (o, i) -> {
    itemConsumer.accept(i);
    return true;
}, (l, r) -> l | r);
if (!hasItems) {
    emptyAction.run();
}

这应该适用于并行流,因为遇到项目的任何流都会将值设置为true

但是,我不确定我喜欢这个,因为它对reduce操作的使用有点迟钝。

另一种方法是使用AtomicBoolean作为可变boolean容器:

final AtomicBoolean hasItems = new AtomicBoolean(false);
stream.forEach(i -> {
    itemConsumer.accept(i);
    hasItems.set(true);
});
if (!hasItems.get()) {
    emptyAction.run();
}

但我不知道我是否更喜欢那样。

最后,您可以让itemConsumer记住状态:

class ItemConsumer implements Consumer<Object> {

    private volatile boolean hasConsumedAny;

    @Override
    public void accept(Object o) {
        hasConsumedAny = true;
        //magic magic
    }

    public boolean isHasConsumedAny() {
        return hasConsumedAny;
    }
}

final ItemConsumer itemConsumer = new ItemConsumer();
stream.forEach(itemConsumer::accept);
if (!itemConsumer.isHasConsumedAny()) {
    emptyAction.run();
}

这似乎有点整洁,但可能不实用。所以也许是一个装饰模式 -

class ItemConsumer<T> implements Consumer<T> {

    private volatile boolean hasConsumedAny;
    private final Consumer<T> delegate;

    ItemConsumer(final Consumer<T> delegate) {
        this.delegate = delegate;
    }

    @Override
    public void accept(T t) {
        hasConsumedAny = true;
        delegate.accept(t);
    }

    public boolean isHasConsumedAny() {
        return hasConsumedAny;
    }
}

final ItemConsumer<Object> consumer = new ItemConsumer<Object>(() -> /** magic **/);

TL; DR:某事必须记住在消费Stream期间是否遇到过任何问题,不管是:

    Stream; 的情况下,
  • reduce本身
  • AtomicBoolean;或
  • 消费者

从逻辑的角度来看,我认为消费者可能是最好的选择。

答案 1 :(得分:2)

没有任何其他变量的解决方案:

stream.peek(itemConsumer).reduce((a, b) -> a).orElseGet(() -> {
    emptyAction.run();
    return null;
});

请注意,如果流是并行的,则可以同时为不同线程中的不同元素调用itemConsumer(例如forEach,而不是forEachOrdered)。如果第一个流元素为空,此解决方案也将失败。

答案 2 :(得分:1)

有一个简单的直接解决方案:

Spliterator<Object> sp=stream.spliterator();
if(!sp.tryAdvance(itemConsumer))
    emptyAction.run();
else
    sp.forEachRemaining(itemConsumer);

如果您愿意,您甚至可以在第一个之后保持对元素的并行支持:

Spliterator<Object> sp=stream.parallel().spliterator();
if(!sp.tryAdvance(itemConsumer))
    emptyAction.run();
else
    StreamSupport.stream(sp, true).forEach(itemConsumer);

在我看来,基于reduce的解决方案更容易理解。

答案 3 :(得分:1)

你可以这样做:

if(stream.peek(itemConsumer).count() == 0){
    emptyAction.run();
}

但是如果count知道Java 9中peek的大小(参见here),Stream可能会被更改为跳过if(stream.peek(itemConsumer).mapToLong(e -> 1).sum() == 0){ emptyAction.run(); } ,所以如果你希望它能够在将来使用:

<div class="companies">
        <ul class="logos">
        <li><img src="images/image1.png" alt="" height="25px" /></li>
        <li><img src="images/image2.png" alt="" height="25px" /></li>
        <li><img src="images/image3.png" alt="" height="25px" /></li>
        <li><img src="images/image4.png" alt="" height="25px" /></li>
        <li><img src="images/image5.png" alt="" height="25px" /></li>
        </ul>
        </div>

/* Logos */
.companies {
    width:100%;
}


ul.logos {
    list-style:none;
    margin:0 auto;
}

ul.logos li {
    display: inline-block;
    padding: 0 1em 0 0;
    vertical-align: middle;
}

答案 4 :(得分:0)

使用reduce的另一种尝试:

    Stream<Object> stream = Stream.of("a","b","c");
    //Stream<Object> stream = Stream.empty();


    Runnable defaultRunnable = () -> System.out.println("empty Stream");
    Consumer<Object> printConsumer = System.out::println;

    Runnable runnable = stream.map(x -> toRunnable(x, printConsumer)).reduce((a, b) -> () -> {
        a.run();
        b.run();
    }).orElse(defaultRunnable);

    runnable.run(); // prints a, b, c (or empty stream when it is empty)


    // for type inference
    static <T> Runnable toRunnable(T t, Consumer<T> cons){ 
        return ()->cons.accept(t);
    }

此方法不使用peek()

Dim i As Long For i = LBound(SampleArray) To UBound(SampleArray) ListBox1.AddItem SampleArray(i) Next