I agree with SimMac's description of the problem, but disagree with his suggested fix.
The problem with saying simply String key = "";
is that you then use this value. Maybe this is OK - that stationMap.get(key)
returns a safe "not found" value - but much more likely it doesn't. Say it returns null
: now you're adding null
to stationsOfBus
, which may fail at runtime (best case - but it may fail a long way from this code), or silently continue to propagate nonsense through your program (worst case).
It is much better to fix the actual problem that was caught by the compiler (that the map might be empty) than to hack a fix which just hides that problem.
If it is the case that busTimesForSingleStation
can't be empty, then assert that fact:
if (!busTimesForSingleStation.isEmpty()) {
String key = ""; // Now you know it will be overwritten.
for (String maybeKey : busTimesForSingleStation.keySet()) {
key = maybeKey;
}
} else {
throw new AssertionError("Should never happen!");
}
Or, if it could be empty, handle that explicitly:
if (!busTimesForSingleStation.isEmpty()) {
String key = ""; // Now you know it will be overwritten.
for (String maybeKey : busTimesForSingleStation.keySet()) {
key = maybeKey;
}
} else {
// Some logic to handle it correctly.
}
The deeper cause of this problem is that your choice of return type of generateTimesForStation
does not accurately encode your intent:
- your question says that you expect the returned map to contain one key/value pair
- your return type says that callers of the method should expect to be returned zero, one or more key/value pairs
Obviously, "one k/v pair" can be represented in a data structure containing "zero or more k/v pairs" - but choosing to so represent it means that calling code must be able to handle the "zero" and "or more" cases too.
For instance, if the returned data structure did contain 2 or more k/v pairs, and you want to pick the "last" key, you'd have to worry about the iteration ordering of those pairs. A requirement to handle such a case is completely removed if the type system can guarantee that it will never occur.
If you want to return exactly one k/v pair, there are two options to highlight:
The simplest change from your code is to use Map.Entry<String, String[]>
instead of Map<String, String[]>
. A Map.Entry
is simply one of the elements of the structure you are iterating over in the loop.
Map.Entry<String, String[]> entry = generateTimesForStation(i);
String key = entry.getKey();
// Do whatever with the key.
Write a custom type to represent the k/v pair, and return and instance of that. One of the disadvantages of Map.Entry
(and, by extension, Map
) is that there is no semantic meaning attached to "key" and "value". It might be easier to read the code at a glance if you have accessors called getStationName
and getTimes
(or whatever). A well-chosen class name is also a lot prettier than the generic soup of Map.Entry<String, String[]>
.
BusTime entry = generateTimesForStation(i);
String key = entry.getStationName();
// Do whatever with the key - or maybe just refer to
// `entry.getStationName()` directly.
Whichever of these you choose, you now avoid the uninitialized variable and loop, since there is nothing to loop over any more.
If you want to return zero or one k/v pairs, the two options above are valid, but you should wrap the result in an Optional
-like type in order to strongly indicate the possibility that no such k/v pairs exists. You could return null
to indicate that, but I would discourage that - see Louis Wasserman's answer on why Optional
should be preferred over null
(he is talking about the Guava Optional
, but the argument for Java 8 Optional
is identical).
Optional<Map.Entry<String, String[]>> entry = generateTimesForStation(i);
if (entry.isPresent()) {
String key = entry.get().getKey();
// Do whatever with the key.
} else {
// Handle the "not found" case.
}
The approach of using a more specific return type may help in your implementation of the generateTimesForStation
too: the constraint that one (or maybe zero or one) values must be returned can help you catch control flow paths which would result in return values that don't meet these constraints.