当前位置: 首页 > 知识库问答 >
问题:

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

吕向阳
2023-03-14

我如何用java8流式API表达这一点?

我想对流中的每个项执行itemcumer。如果没有要执行的项目,则清空操作。

我当然可以写这样的东西:

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);

但我更愿意避免任何Lists。

我还考虑过在peek方法中设置一个标志,但该标志是非最终的,因此是不允许的。使用布尔容器似乎也是一种太多的变通方法。

共有3个答案

汪栋
2023-03-14

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

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的解决方案更容易理解。

厍和颂
2023-03-14

没有任何附加变量的解决方案:

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

注意,如果流是并行的,那么可以为不同线程中的不同元素同时调用itemcumer(例如在forEach中,而不是在forEachOrdered中)。此外,如果第一个流元素为null,该解决方案将失败。

翁和颂
2023-03-14

您可以强制reduce来执行此操作。逻辑是在遇到任何有用数据时,将值设置为“真”。

然后,reduce的结果为false,则未遇到任何项目。如果遇到任何项目,则结果将为true:

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

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

然而,我不确定我是否喜欢这个,因为这是一个稍微迟钝的减少操作。

另一种方法是使用原子布尔作为可变的布尔容器:

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

然而,我不知道我是否或多或少喜欢这个。

最后,您可以让您的项目消费者记住状态:

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 **/);

太长别读:有些东西必须记住您在使用Stream期间是否遇到了什么,可以是:

  • Stream本身在duce的情况下;
  • 原子布尔;或
  • 消费者

我认为从逻辑的角度来看,消费者可能处于最佳位置。

 类似资料:
  • 我正在尝试在php中创建以下元素,以创建我们的一个客户所需的xml。 每件事都很好,但我不知道如何创建以下元素 我已经尝试了几种方法,但仍然无法正确生成上述示例 有人能送我上路吗? 这是我的代码

  • 另外,我不想要任何jQuery。

  • 问题内容: 我需要解析一个连续的格式良好的XML元素流,仅向其提供一个已经构造的对象。这些元素没有包含在根元素中,也没有以XML标头开头,例如,但它们都是有效的XML。 使用Java 类是行不通的,因为XML Reader希望从封闭的根元素开始解析格式良好的XML。因此,它只是读取流中的第一个元素(它被视为根元素),而在下一个元素中失败,使用典型 org.xml.sax.SAXParseExcep

  • 问题内容: 我想知道如何使用JavaScript(而不是jQuery)选择没有特定类的元素。 例如,我有以下列表: 然后通过以下方式选择完成的任务: 但是然后我不确定如何选择没有这些类的列表项。 问题答案: 这将选择第二个元素。 要么 例:

  • 在端口20436上启动ChromeDriver(V2.9.248315)线程“main”org.openqa.selenium.noSuchelementException异常:没有这样的元素 (会话信息:chrome=53.0.2785.143)(驱动程序信息:ChromeDriver=2.9.248315,platform=Windows NT 6.1 SP1 x86_64)(警告:服务器没有

  • 问题内容: 我正在尝试通过JavaScript / CSS修改页面(很像Fashiony或Greasemonkey一样)。这是一个非常复杂的页面(我没有构建或无法修改预渲染),这使得构造CSS选择器变得很困难(手动查看文档结构)。我该如何实现? 问题答案: 在安装了FireBug的情况下使用FireFox。 右键单击任何元素 选择“检查元素” 右键单击HTML树中的元素 选择“复制XPath”或“