Andrea Giuliano
Andrea Giuliano

Reputation: 378

Java8 transform a [List<Object>, String] in a Map<Object, String>

I have a List<Computer>. Every Computer has a list of CPU and a hostname. So,suppose I have:

List<Computer> computers

I can call

List<CPU> CPUs = computer.getCPUs();

and I can call

String hostname = computer.getHostName();

What I want to do is, using Streams, obtain a Map that contains as key the CPU and as String the hostname. Same CPU inside the same Computers will replicate the hostname.

How can I do that?

Pre Java8 code would be this:

public Map<CPU, String> getMapping(List<Computer> computers) {
    Map<CPU, String> result = new HashMap<>();
    for (Computer computer : computers) {
        for (CPU cpu : computer.getCPUs()) {
            result.put(cpu, computer.getHostname());
        }
    }

    return result;
}

Upvotes: 3

Views: 159

Answers (3)

shmosel
shmosel

Reputation: 50716

You can do it using an intermediate Entry to hold the CPU and hostname together:

Map<CPU, String> map = computers.stream()
        .flatMap(c -> c.getCPUs().stream().map(cpu -> new AbstractMap.SimpleEntry<>(cpu, c.getHostName())))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Upvotes: 1

Nicolas Filotto
Nicolas Filotto

Reputation: 44965

You could do it by implementing your own Collector in order to assign the same value to all the CPUs of the same computer:

Map<CPU, String> cpus = computers.stream().collect(
    Collector.of(
        HashMap::new,
        // Put each cpu of the same computer using the computer's hostname as value
        (map, computer) -> computer.getCPUs().stream().forEach(
            cpu -> map.put(cpu, computer.getHostName())
        ),
        (map1, map2) -> { map1.putAll(map2); return map1; }
    )
);

This is basically the equivalent of what you currently do using the Stream API, the only difference is the fact that you could parallelize it by simply using a parallel stream instead of a normal stream, but in this particular case as the tasks are quite small, it would probably not help much in term of performances such that using the Stream API in this case could be considered as a little bit abusive.

Upvotes: 1

diesieben07
diesieben07

Reputation: 1547

If your CPU class has a back-reference to it's Computer instance, then you can do this easily. First stream over all the computers, and flat-map with getCPUs, this will give you a Stream<CPU> of all CPUs. Then you can use Collectors.toMap to collect into a Map<CPU, String> using Function.identity for the key and a lambda extracting first the Computer and then the hostname from the CPU for the value. In code:

computers.stream()
    .flatMap(computer -> computer.getCPUs().stream())
    .collect(Collectors.toMap(Function.identity(), cpu -> cpu.getComputer().getHostname()));

Upvotes: 1

Related Questions