OCP Question 99, Explanation

Given:

class Car {
    private List<Wheel> wheels;
    public Car(){
        wheels = Arrays.asList(
                new Wheel(),
                new Wheel(),
                new Wheel(),
                new Wheel());
    }
    public List<Wheel> getWheels() {
        return wheels;
    }
}

class Wheel {
    private int airPressure;
    public Wheel(){
        airPressure = (int)(Math.random()*100);  // sets airPressure randomly
    }                                            // from 0 to 99 inclusive
    public int getAirPressure() {
        return airPressure;
    }
}

class Test{
    public static void main(String[] args) {
        List<Car> cars = Arrays.asList(new Car(), new Car(), new Car());
        System.out.println(cars.stream()
                               .map(Car::getWheels)                // line n1
                               .flatMap(Wheel::stream)             // line n2
                               .mapToInt(Wheel::getAirPressure)    // line n3
                               .max()                              // line n4
                               .isPresent()                        // line n5
        );
   }
}

Which two modifications, when applied together, will let the code find the maximum value of air pressure of all wheels in all the cars?

A. Remove line n5
B. Replace line n5 with
.orElse(12345)

C. Replace line n2 with
.flatMapToInt(List::stream)
D. Replace line n2 with
.flatMap(List::stream)

E. Replace line n3 with
.flatMap(Wheel::getAirPressure)
F. Replace line n5 with
.ifPresent(Wheel::getAirPressure)

 

The correct answer is BD.

 

Instead of checking each option one by one, let’s build our own stream pipeline and see how it differs from the code.

Okay, first of all we’ll need a data structure that holds all Wheels; this is achieved by creating a stream of Cars and then “extracting” from each element its representation that we’re interested in: in other words, by mapping each Car to its own set of Wheels (line n1).

Now we have a List of Wheels each of which we are going to check for its air pressure, and this can be done only after we “extract” all Wheels one by one from that List. Line n2, however, wants to extract Wheels from Wheel, which is not a List, so replacing Wheel with List (option B) does exactly what we need.

But why are we using flatMap() rather map() at line n2? After all, both methods return a Stream:

This is how Stuart Marks explains the crucial difference between these two methods:

…the map operation produces one output value for each input value, whereas the flatMap operation produces an arbitrary number (zero or more) values for each input value.

Accordingly, since we have THREE input values – do you see it? line n1 extracts THREE wheels objects because we have THREE cars, – using map(List::stream) rather than flatMap(List::stream) at line n2 would give us THREE Streams of FOUR Wheels each instead of a single Stream of TWELVE Wheels…

So after flatMap(List::stream) we have all the wheels we need and then we extract air pressure value from each Wheel by mapping these objects to their airPressure field, which is of type int, that’s why we use mapToInt() and its method max().

Remember the FIMI MARE rule (ref.to Problem 87)? It says that the max() method returns an Optional, which explains why we see isPresent(). This method, however, returns just a boolean (observe how it follows the JavaBeans Specification, §8.3.2, Boolean properties) while we need a concrete number. Out of all Optional methods we are offered orElse(), which does exactly what we want although its default value does look strange…

 

Additional tasks:

Suggest how to filter out those cars where air pressure is below a certain threshold:

  • simultaneously in all four wheels,
  • in any wheel.

 

Click here to view answer.

The following code snippet selects only those cars where air pressure in all the wheels is below 90:

List<Car> badCars = cars.stream()
                        .filter(car -> car.getWheels()
                                          .stream()
                                          .allMatch(wheel -> wheel.getAirPressure() < 90)
                               )
                        .collect(Collectors.toList());

And this is how we could filter out cars where at least one wheel has its air pressure below 10:

                                          .anyMatch(wheel -> wheel.getAirPressure() < 10)

Leave Comment

Your email address will not be published.