Java, Map and Optional

Posted by & filed under , .

ScriptingI do like the Optional class in Java — it is a long awaited elegant replacement for returning null whenever something isn’t there then relying on if( x != null ) checks or using ?: in the format (x == null) ? "foo" : "bar". It works great also with the new Java stream classes and offers some functional methods of its own (flatMap and filter for instance).

The annoying bit is that some of the collection classes in the JDK haven’t really caught up with either this or the streams approach — specifically Map interface. Retrieving a value by key from a Map and checking if it’s there or not doesn’t make use of Optional for instance, which is one of my annoyances with the JDK. Of course, if you read my previous posts about dealing with values not present in a Map you have a few ways around it such as getOrDefault, computeIfAbsent etc. but there is nothing that can return you an Optional.

There is however a way to throw Optional in the game when dealing with Map instances — and that is based on Optional.ofNullable: the contract for Map.get() states:

Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.

Which is why Map.get() calls get followed ever so often with if( value == null ) ...

However, if you prefer using Optional to do all the checking for value present or not, you can wrap up the call in Optional.ofNullable() — which as the docco states will return an Optional.empty() if the value passed in is null! — then write your code in a more functional matter from there on such as:

Optional<Integer> findTotalWagesInADepartmentInACountry(String country, String department, Map<String, Department> allPeople) {
   return Optional.ofNullable(allPeople.get(department)) //get all the people who are or were working in this department
                  .filter((d) -> !country.equals(d.getLocation())) // only look at the country requested
                  .flatMap((d) -> d.getEmployeesList()) // retrieve all employees
                  .flatMap((lst) -> getTotalSalary(lst));