假设我有一个庞大的 Boolean
数组flags
:
Boolean[] flags = { true, false, true }; // 3 means "many"
我想做两件事flags
:
检查所有元素是否都true
返回并返回指标;
将所有元素重置为false
.
使用Java 8的lambda表达式,我可以按如下方式执行:
indicator = Arrays.stream(flags).allMatch(flag -> flag); Arrays.stream(flags).forEach(flag -> flag = false); return indicator;
但是,此实现扫描flags
两次.由于flags
是巨大的,我不希望这样.另外,我更喜欢lambda方式.有没有办法checkIfAllTrueAndReset
用(单行)lambda表达式实现这种语义,flags
只扫描一次?
相关但不相同:检查布尔数组中的所有值是否为真的最优雅的方法是什么?
注意:我从评论和答案中学到了很多东西.谢谢你们!
Stream
很酷,但不是这个.
BitSet
(以及它的位原子对应物AtomicBitSet
)更适合这种情况(因此被接受为答案;感谢其他人).
不鼓励map
(Stream
或通常是函数式编程)的副作用.
Arrays.stream(flags).forEach(flag -> flag = false)
(在我的代码中)没有设置任何东西!
Maroun.. 16
使用BitSet
该类的经典示例:
该类实现了一个根据需要增长的位向量.位组的每个组件都有一个布尔值.
在复杂性方面,BitSet
每个boolean
值使用~1位,这比使用大型Boolean
对象更好.
关于是否设置所有位的检查(真或假),API提供了许多有用的方法 - 并且它们非常有效.
使用BitSet
该类的经典示例:
该类实现了一个根据需要增长的位向量.位组的每个组件都有一个布尔值.
在复杂性方面,BitSet
每个boolean
值使用~1位,这比使用大型Boolean
对象更好.
关于是否设置所有位的检查(真或假),API提供了许多有用的方法 - 并且它们非常有效.
这对于流和lambdas来说是一个糟糕的匹配,并没有很好的方法来做到这一点.
问题是,蒸汽使用集合的元素,但您需要修改实际的集合.
我认为你最好使用旧式for循环.
一个不太好的解决方案是在数组的索引上获取流,并使用其中的一个forEach
循环遍历数组.一个AtomicBoolean
可用于存储在所有元素都是true
.这可以并行运行,但我认为普通的for循环更好.
例:
Boolean[] flags = { true, true, true }; AtomicBoolean allTrue = new AtomicBoolean(true); IntStream.range(0, flags.length) .forEach(ix -> { if (!flags[ix]) allTrue.set(false); flags[ix] = false; });
在该问题的评论中,有人提到它可能是一个有趣的解决方案AtomicBoolean
.在这种情况下,可以使用具有流的解决方案,因为可以在不修改原始集合的情况下重置元素.
然而,解决方案确实有问题,所以最好不要使用它.问题是map
用于副作用(重置值)和映射操作(提取旧值).我认为可以并行运行它,但它可能比正常的for循环慢,因为每个元素的操作都很小.
另请注意,allMatch
由于该操作是短路的,因此无法使用,如果发现false
它将终止,后续元素将不会被重置.
AtomicBoolean[] flags = { new AtomicBoolean(true), new AtomicBoolean(false), new AtomicBoolean(true) }; boolean allTrue = Stream.of(flags) .map(b -> b.getAndSet(false)) .reduce(true, (a, b) -> a && b);
如果你真的想用一行中的流做这个,那么你可以做这样的事情
boolean allTrue = IntStream.range(0, flags.length).reduce(0, (result, i) -> flags[i] ^ (flags[i] = false) ? result : 1) == 0;
然而,这看起来并不是一个好主意,我倾向于使用BitSet
@MarounMaroun建议的