Build a list of maximum values from a set of lists nested inside nested maps with lambda

Stefano :

I'm new to java 8 and i need to rewrite an old piece of code to implement a new alghoritm. The task is to filter the object which has max speed for each list. Lists are nested inside maps : a root map of roads , which contains maps of road segments and each map of road segments contains a list of objects , each one decribing measured speed at a time interval . I need to find all the maximum speeds for each list .

I've found the following links which looks like my problem, but i cannot adapt them, and i'm not sure my attempts solve my problem correctly.

How do you filter nested loops using Java 8 streams and filters?

Java 8 Filter Map<String,List<Employee>>

Filter on map of map

this is the code i've used as example to write mine


Map<String, Map<String, Employee>> employeeMap =
                            employeeMap.entrySet()
                                       .stream()
                                       .collect(toMap(Map.Entry::getKey,
                                                      e -> e.getValue().entrySet().stream()
                                                      .filter(emp -> !emp.getValue().getState().equals("MI"))
                                                      .collect(toMap(Map.Entry::getKey, Map.Entry::getValue))));


for(Car car : cars) {
                        for (Engine engine : car.getEngines()) {
                            for (Part part : engine.getParts()) {
                                // ...
                            }
                        }
                    }


                    cars.stream()
                        .flatMap(car -> car.getEngines().stream())
                        .flatMap(engine -> engine.getParts().stream())
                        .forEach(part -> {  ... });


this is my resulting code



Map<Integer, Map<Integer, List<PartialSpeed>>> speedsPerRoadPerSegment; 


ArrayList<PartialSpeed> maxSpeedPerSegment = new ArrayList<PartialSpeed>();
                    speedsPerRoadPerSegment.entrySet().stream()
                            .forEach(road-> road.getValue().entrySet()
                            .stream()
                            .forEach(segment ->
                            {
                                Optional<PartialSpeed> result = segment.getValue().stream()
                                .max(Comparator.comparing(PartialSpeed::getnVelFfs));
                                maxSpeedPerSegment.add(result.get());
                            }));


All my attempt to adhere to the example had problems about result types not being recognized. This is the best (not working) i'm been able to achieve :

speedsPerRoadPerSegment.entrySet().stream()
                            .flatMap(road-> road.getValue().entrySet()
                            .stream().flatMap(
                                    segment -> segment .getValue()
                                    .stream()
                                    .max(Comparator.comparing(PartialSpeed::getnVelFfs))
                                    ));

As you can see i've just replaced the old for loops with foreaches. I'd like to understand how to obtain the list of maximum elements directly from the lambda expression.

Holger :

Generally, whether you use a loop or stream, when processing a Map, you should pick the right collection view, keySet(), entrySet(), or values(), depending on which elements you actually need. Since you are only interested in the values, don’t use entrySet().

Then, apply the right terminal operation to the stream, to get the final result:

List<PartialSpeed> maxSpeedPerSegment =
    speedsPerRoadPerSegment.values().stream()
        .flatMap(m -> m.values().stream())
        .map(list -> Collections.max(list, Comparator.comparing(PartialSpeed::getnVelFfs)))
        .collect(Collectors.toList());

This uses Collections.max instead of a nested Stream operation, which will throw an exception if the collection is empty (just like calling get() on an Optional unconditionally will do).

If empty lists may occur, you may filter them before-hand:

List<PartialSpeed> maxSpeedPerSegment =
    speedsPerRoadPerSegment.values().stream()
        .flatMap(m -> m.values().stream())
        .filter(list -> ! list.isEmpty())
        .map(list -> Collections.max(list, Comparator.comparing(PartialSpeed::getnVelFfs)))
        .collect(Collectors.toList());

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=113289&siteId=1