Java可选流的可选流

时间:2015-05-12 21:52:35

标签: java functional-programming

我需要将Stream<Optional<Integer>>转换为Optional<Stream<Integer>>

Optional<Stream<Integer>>的至少一个值为空时,输出Stream<Optional<Integer>>应为空值。

您知道解决问题的任何功能方法吗?我尝试使用collect方法,但没有成功。

4 个答案:

答案 0 :(得分:4)

Well, the tricky thing here is that if you're just given a if current_admin_user.role?(:admin) f.input :invoice_file_date, as: :datepicker if f.object.invoice_file.present? else f.input :invoice_file_date, as: :datepicker, :input_html => { :disabled => true } if f.object.invoice_file.present? end , you can only use it once.

To be stateless and avoid redundant copying, one way is to just catch Stream:

NoSuchElementException

A simple inversion would be:

static <T> Optional<Stream<T>> invert(Stream<Optional<T>> stream) {
    try {
        return Optional.of(
            stream.map(Optional::get)
                  .collect(Collectors.toList())
                  .stream());
    } catch (NoSuchElementException e) {
        return Optional.empty();
    }
}

But to find out if it contains an empty element, you need to actually traverse it which also consumes it.

If you're given the source of the stream, you can traverse it without collecting it:

static <T> Optional<Stream<T>> invert(Stream<Optional<T>> stream) {
    return Optional.of(stream.map(Optional::get));
}
static <T> Optional<Stream<T>> invert(
        Supplier<Stream<Optional<T>>> supplier) {
    // taking advantage of short-circuiting here
    // instead of allMatch(Optional::isPresent)
    return supplier.get().anyMatch(o -> !o.isPresent()) ?
        Optional.empty() : Optional.of(supplier.get().map(Optional::get));
}

That's probably a more interesting approach. (But it's prone to a race condition because the List<Optional<Integer>> myInts = Arrays.asList(Optional.of(1), Optional.of(2), Optional.of(3)); Optional<Stream<Integer>> inverted = invert(myInts::stream); is taken twice. If some other thread adds an empty element in between and gets away with it, we have a problem.)

答案 1 :(得分:1)

虽然已经回答这个问题尚未添加到列表中,但 Java-9 引入Optional.stream,这应该是可以实现的:

// initialized stream of optional
Stream<Optional<Integer>> so = Stream.empty(); 

// mapped stream of T 
Stream<Integer> s = so.flatMap(Optional::stream);   

// constructing optional from the stream
Optional<Stream<Integer>> os = Optional.of(s); 

答案 2 :(得分:0)

Similar to Radiodef's answer, though this one avoids the exception handling and the intermediate list.

0.ToOrdinalWords() => "zeroth"
1.ToOrdinalWords() => "first"
2.ToOrdinalWords() => "second"
8.ToOrdinalWords() => "eighth"
10.ToOrdinalWords() => "tenth"
11.ToOrdinalWords() => "eleventh"
12.ToOrdinalWords() => "twelfth"
20.ToOrdinalWords() => "twentieth"
21.ToOrdinalWords() => "twenty first"
121.ToOrdinalWords() => "hundred and twenty first"

The way this works is it maps to a Stream of Optional Streams of T. The private static <T> Optional<Stream<T>> invertOptional(Stream<Optional<T>> input) { return input.map(integer -> integer.map(Stream::of)) .collect(Collectors.reducing((l, r) -> l.flatMap(lv -> r.map(rv -> Stream.concat(lv, rv))))) .orElse(Optional.empty()); } is used in this case, so each one of the Optional.map items in the resultant stream is a either a Stream of 1, or an empty Optional.

Then it collects these streams by reducing them together. the Optional<Stream<T>> will return an empty Optional if l is empty or the l.flatMap returns an empty. if r.map isn't empty, it calls the Stream.concat, which combines the left and right stream values.

The whole collect reduction produces an r.map, so we narrow that down with the Optional<Optional<Stream<T>>>

Note: Code is tested, and appears to work. The unspecified "edge case" of an empty input Stream is treated as an an empty Optional, but can be easily changed.

答案 3 :(得分:0)

    final Stream<Optional<Integer>> streamOfInts = Stream.of(Optional.of(1), Optional.of(2), Optional.of(3), Optional.of(4), Optional.of(5));

    // false - list of Optional.empty(); true -> list of Optional.of(Integer)
    final Map<Boolean, List<Optional<Integer>>> collect = streamOfInts.collect(Collectors.partitioningBy(Optional::isPresent));

    final Function<List<Optional<Integer>>, Stream<Integer>> mapToStream = List->List.stream().filter(o->o.isPresent()).map(o->o.get());

     Optional<Stream<Integer>> result = Optional
             .of(Optional.of(collect.get(false)).filter(list->list.size()>0).orElse(collect.get(true)))
             .filter(list->list.size()>0)
             .filter(list->list.get(0).isPresent())
             .map(mapToStream)
             .map(Optional::of)
             .orElse(Optional.empty());