Преобразование объекта [] из булевых в логическое [] с использованием потоков

Я могу сделать преобразование с кодом следующим образом:

Object[] array = (Object[]) message.get(key);
boolean[] result = new boolean[array.length];
for (int i = 0; i < array.length; i++) {
    result[i] = (boolean) array[i];
}

Но я думал, что можно сделать то же самое преобразование, используя потоки Java 8. Я начинаю кодировать что-то вроде этого:

boolean[] =  Arrays.stream(array)
                   .map(Boolean.class::cast)
                   .map(Boolean::booleanValue)
                   .toArray()

Но этот код не работает. Компилятор говорит

incompatible types: java.lang.Object[] cannot be converted to boolean[]

Я пытаюсь понять, в чем проблема с кодом. Я думаю, что map(Boolean::booleanValue) вернет поток логических значений, которые я могу собрать с помощью toArray.

Ответы

Ответ 1

Нет, потому что map(Boolean::booleanValue) расширяется до

map((Boolean value) -> (Boolean)value.booleanValue());

(обратите внимание на автобоксинг, вставленный компилятором, потому что функция, переданная в map(), должна вернуть Object, а не boolean)

Вот почему потоки Java8 включают целую кучу специализированных классов (IntStream, LongStream и DoubleStream). К сожалению, для boolean нет специализированного класса потока.

Ответ 2

Если вы не против идеи получения списка, вы можете просто выполнить collect в качестве действия вашего терминала:

final List<Boolean> boolList = Arrays.stream(array)
                                     .map(Boolean.class::cast)
                                     .map(Boolean::booleanValue)
                                     .collect(Collectors.toList());

Поток, определенный здесь, способен производить Object[] только при попытке собрать его в массив. Использование списка по крайней мере позволит вам сохранить тип, в который вы хотите преобразовать его.

Ответ 3

Единственными потоками примитивного типа являются IntStream, LongStream и DoubleStream. В частности, нет BooleanStream, и нет идиоматического способа преобразования a Stream<Boolean> в boolean[].

Если вам нужна эта функция часто, вы можете использовать служебный класс следующим образом:

public final class BooleanUtils {

    private BooleanUtils() {}

    public static boolean[] listToArray(List<Boolean> list) {
        int length = list.size();
        boolean[] arr = new boolean[length];
        for (int i = 0; i < length; i++)
            arr[i] = list.get(i);
        return arr;
    }

    public static final Collector<Boolean, ?, boolean[]> TO_BOOLEAN_ARRAY
         = Collectors.collectingAndThen(Collectors.toList(), BooleanUtils::listToArray);
} 

Тогда, учитывая a Stream<Boolean>, вы сможете:

boolean[] arr = stream.collect(BooleanUtils.TO_BOOLEAN_ARRAY);

Ответ 4

Я думаю, стоит также отметить, что есть toArray(IntFunction<A[]> generator), который по крайней мере позволит вам получить окончательный результат Boolean[], который ближе к тому, что вы хотели.

Boolean[] boolArray =  Arrays.stream(array)
               .map(Boolean.class::cast)
               .map(Boolean::booleanValue)
               .toArray(Boolean[]::new);

Ответ 5

В моей бесплатной библиотеке StreamEx есть коллекционер toBooleanArray, который создает примитивный булев массив:

boolean[] result = Arrays.stream(array)
                         .collect(MoreCollectors.toBooleanArray(obj -> (boolean)obj));

Если вам не нравится использование сторонней библиотеки, вы можете реализовать такой сборщик самостоятельно:

public static <T> Collector<T, ?, boolean[]> toBooleanArray(Predicate<? super T> predicate) {
    class Acc {
        BitSet bs = new BitSet();
        int count = 0;
    }
    return Collector.of(Acc::new, (acc, t) -> acc.bs.set(acc.count++, predicate.test(t)),
            (acc1, acc2) -> {
                acc2.bs.stream().forEach(i -> acc1.bs.set(i + acc1.count));
                acc1.count += acc2.count;
                return acc1;
            }, acc -> {
                boolean[] res = new boolean[acc.count];
                acc.bs.stream().forEach(i -> res[i] = true);
                return res;
            });
}

Он хорошо работает для последовательных и параллельных потоков и вполне совместим с памятью.

Ответ 6

Java-примитивные типы не являются ссылочными типами, поэтому вы не можете напрямую преобразовывать Boolean в Boolean (autoboxing and unboxing были добавлены к языку в Java 1.5 и не работают с массивами типов-оболочек для примитивных типов). Однако вы можете использовать IntStream.range(int, int), например

boolean[] result = new boolean[array.length];
IntStream.range(0, array.length).forEach(x -> result[x] = (boolean) array[x]);