Проверка, если все true и reset массив Boolean [] с использованием однострочного лямбда-выражения Java 8

Предположим, что у меня массив огромный Boolean flags:

Boolean[] flags = { true, false, true };    // 3 means "many"

Я хочу сделать две вещи на flags:

  • проверьте, все ли элементы true и возвращают индикатор;
  • reset все элементы false.

Используя лямбда-выражение Java 8, я могу сделать это следующим образом:

indicator = Arrays.stream(flags).allMatch(flag -> flag);
Arrays.stream(flags).forEach(flag -> flag = false);
return indicator;

Однако эта реализация сканирует flags дважды. Поскольку flags огромен, я не хочу этого. Кроме того, я предпочитаю лямбда-способ. Есть ли способ реализации этой семантики checkIfAllTrueAndReset с (однострочным) лямбда-выражением, которое сканирует flags только один раз?


Связано, но не одно и то же: Каков самый элегантный способ проверить, истинны ли все значения в булевом массиве?


Примечание: Я многому учусь из комментариев и ответов. Спасибо всем!

  • Stream классный, но это не для этого.
  • BitSet (и его бит-разумно атомный аналог AtomicBitSet) более подходит для этого (так принято, как ответ, спасибо другим).
  • Побочные эффекты в map (Stream или вообще функциональное программирование) обескуражены.
  • Arrays.stream(flags).forEach(flag -> flag = false) (в моем коде) ничего не задает!

Ответ 1

Классический пример использования BitSet class:

Этот класс реализует вектор бит, который растет по мере необходимости. Каждый компонент битового набора имеет логическое значение.

В терминах сложности BitSet использует ~ 1 бит для каждого значения boolean, что намного лучше, чем использование большого массива объектов boolean.

Что касается проверки всех битов, установленных или нет (true или false), API предоставляет множество полезных методов - и они действительно эффективны.

Ответ 2

Это плохое совпадение для потоков и lambdas, и нет действительно хорошего способа сделать это.

Проблема заключается в том, что пары работают с элементами коллекции, но вам нужно изменить фактическую коллекцию.

Я думаю, что для этого лучше всего использовать старую школу для цикла.


Одно не очень приятное решение состоит в том, чтобы получить поток по индексам массива и использовать forEach для цикла по массиву. AtomicBoolean можно использовать для хранения, если все элементы true. Это можно запустить параллельно, но я думаю, что обычный для цикла лучше.

Пример:

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. В этом случае возможно решение с потоками, поскольку элемент может быть reset без изменения исходной коллекции.

Решение действительно вызывает сомнения, поэтому, вероятно, лучше не использовать его. Проблема в том, что map используется как для побочного эффекта (сброса значения), так и для операции сопоставления (извлечения старого значения). Я думаю, что можно запустить его параллельно, но он, вероятно, медленнее обычного цикла for, поскольку операция над каждым элементом настолько мала.

Также обратите внимание, что allMatch не может использоваться, так как эта операция коротко замыкается, и если она найдет false, она завершится, а последующие элементы не будут reset.

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

Ответ 3

Если вы действительно хотите сделать это с потоками в одной строке, вы можете сделать что-то вроде этого

boolean allTrue = IntStream.range(0, flags.length).reduce(0,
    (result, i) -> flags[i] ^ (flags[i] = false) ? result : 1) == 0;

Однако это не выглядит хорошей идеей, и я склоняюсь к использованию BitSet, как это было предложено @MarounMaroun

Ответ 4

Если вы знаете размер массива заранее, вы можете проверить время O (1), если все флаги истинны. Сохраните размер массива в переменной arraySize и установите дополнительную переменную indexCount в 0. Вместо изменения флагов в массиве до true/false, увеличивайте/уменьшайте переменную indexCount. Если вы хотите узнать, истинны ли все флаги, просто отметьте arraySize == indexCount. Если вам не нужно знать текущий флаг определенных элементов, вы можете полностью отказаться от массива.

Это также применимо, если вам нужно проверить, действительно ли определенное количество флагов.