Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Java 8, how to get Stream<T> from Stream<? extends Collection<T>>?

In Java 8, using the packages java.util.function and java.util.stream as well as the new language features lambda and method references, what is the best way to turn a Stream<Collection<T>> into a Stream<T>?

Here's the example with the solution that I've found so far but am unhappy with. In order to create a Stream<T> from a Stream<Collection<T>>, I use collect().stream() with an intermediate HashSet (my Collection is a Set).

import java.security.Provider;
import java.util.HashSet;
import static java.lang.System.out;
import static java.security.Security.getProviders;
import static java.security.Provider.Service;
import static java.util.Arrays.stream;

public class ListMessageDigests {
    public static void main(final String... args) {
        stream(getProviders())
            .map(Provider::getServices)
            .collect(HashSet<Service>::new, HashSet::addAll, HashSet::addAll)
            .stream()
            .filter(service -> "MessageDigest".equals(service.getType()))
            .map(Service::getAlgorithm)
            .sorted()
            .forEach(out::println);
    }   
}   

Is there a more elegant way to convert a Stream<Collection<T>> to a Stream<T>? Like a Stream<Set<Service>> in this example into a Stream<Service>? I am not happy with using an intermediate HashSet and .collect().stream(), it feels to complicated to me.

P.S.: I know that I could've simply done this:

import static java.security.Security.getAlgorithms;
public class ListMessageDigests {
    public static void main(final String... args) {
        getAlgorithms("MessageDigest")
            .forEach(out::println);
    }
}

I was only using getProviders() and getServices to quickly construct a Stream<Set<Service>> to have a Stream<Collection<T>> to demonstrate the question.

like image 328
Christian Hujer Avatar asked Jan 09 '23 05:01

Christian Hujer


1 Answers

You can use flatMap with Collection#stream

<T> Stream<T> flatten(Stream<? extends Collection<? extends T>> stream){
    return stream.flatMap(Collection::stream);
}

The final code would then look like this:

import static java.lang.System.out;
import static java.security.Provider.Service;
import static java.security.Security.getProviders;
import static java.util.Arrays.stream;

public class ListMessageDigests {
    public static void main(final String... args) {
        stream(getProviders())
            .flatMap(p -> p.getServices().stream())
            .distinct()
            .filter(service -> "MessageDigest".equals(service.getType()))
            .map(Service::getAlgorithm)
            .sorted()
            .forEach(out::println);
    }
}

Note that without calling distinct this might not result in a stream with exactly the same elements as your example, since collecting into a set will remove duplicates if there are any.

like image 71
Alex - GlassEditor.com Avatar answered Jan 18 '23 08:01

Alex - GlassEditor.com