Возвращать пустой элемент из операции с картой Java 8
Использование потока Java 8: лучший способ отобразить List<Integer>
, когда у вас нет вывода для ввода Integer?
Просто вернуть null? Но теперь размер моего выходного списка будет меньше моего размера ввода...
List<Integer> input = Arrays.asList(0,1,2,3);
List<Integer> output = input.stream()
.map(i -> {
Integer out = crazyFunction(i);
if(out == null || out.equals(0))
return null;
return Optional.of(out);
})
.collect(Collectors.toList());
Ответы
Ответ 1
Я не понимаю, почему вы (и все ответы) делаете это настолько сложным. У вас есть операция отображения и операция фильтрации. Таким образом, самый простой способ - просто применить эту операцию один за другим. И если ваш метод уже возвращает Optional
, нет необходимости иметь дело с Optional
.
input.stream().map(i -> crazyFunction(i))
.filter(out -> out!=null && !out.equals(0))
.collect(Collectors.toList());
Это может быть упрощено до
input.stream().map(context::crazyFunction)
.filter(out -> out!=null && !out.equals(0))
.collect(Collectors.toList());
Но у вас, похоже, есть более теоретический вопрос о том, какой тип List
для генерации, один с заполнителями для отсутствующих значений или один с другим размером, чем список ввода.
Простой ответ: не создавайте список. A List
не является самоцелью, поэтому вы должны рассмотреть, для какой операции вам нужен этот список (или его содержимое), и применить право операции как операцию терминала потока. Затем у вас есть ответ, поскольку операция определяет, должны ли отсутствующие значения отфильтровываться или быть представлены специальным значением (и какое значение оно должно быть).
Это может быть другой ответ для разных операций...
Ответ 2
Замените вызов map
на flatMap
. Операция map
создает одно выходное значение для каждого входного значения, тогда как операция flatMap
производит любое количество выходных значений для каждого входного значения - включает ноль.
Самый простой способ - это, вероятно, заменить проверку следующим образом:
List<Integer> output = input.stream()
.flatMap(i -> {
Integer out = crazyFunction(i);
if (out == null || out.equals(0))
return Stream.empty();
else
return Stream.of(out);
})
.collect(Collectors.toList());
Дальнейший рефакторинг может изменить crazyFunction
, чтобы он возвращал Optional
(возможно, OptionalInt
). Если вы вызываете его из map
, результат будет Stream<OptionalInt>
. Затем вам нужно flatMap
передать этот поток для удаления пустых опций:
List<Integer> output = input.stream()
.map(this::crazyFunctionReturningOptionalInt)
.flatMap(o -> o.isPresent() ? Stream.of(o.getAsInt()) : Stream.empty())
.collect(toList());
Результатом flatMap
является Stream<Integer>
, который помещает в поле int
s, но это нормально, так как вы собираетесь отправить их в List
. Если вы не собираетесь вставлять значения int
в List
, вы можете преобразовать Stream<OptionalInt>
в IntStream
, используя следующее:
flatMapToInt(o -> o.isPresent() ? IntStream.of(o.getAsInt()) : IntStream.empty())
Для дальнейшего обсуждения работы с потоками опций, см. этот вопрос и его ответы.
Ответ 3
Упрощенные варианты ответа @Martin Magakian:
List<Integer> input = Arrays.asList(0,1,2,3);
List<Optional<Integer>> output =
input.stream()
.map(i -> crazyFunction(i)) // you can also use a method reference here
.map(Optional::ofNullable) // returns empty optional
// if original value is null
.map(optional -> optional.filter(out -> !out.equals(0))) // return empty optional
// if captured value is zero
.collect(Collectors.toList())
;
List<Integer> outputClean =
output.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList())
;
Ответ 4
Вы можете вывести вывод в Optional, который может содержать или не содержать ненулевое значение.
С выходом: return Optional.of(out);
Без вывода: return Optional.<Integer>empty();
Вы должны включить в параметр, потому что массив не может содержать нулевое значение.
List<Integer> input = Arrays.asList(0,1,2,3);
List<Option<Integer>> output = input.stream()
.map(i -> {
Integer out = crazyFunction(i);
if(out == null || out.equals(0))
return Optional.<Integer>empty();
return Optional.of(out);
})
.collect(Collectors.toList());
Это сделает input.size() == output.size()
.
Позже вы можете исключить пустую опцию, используя:
List<Integer> outputClean = output.stream()
.filter(Optional::isPresent)
.map(i -> {
return i.get();
})
.collect(Collectors.toList());