SortedMap.subMap
This is the API for SortedMap<K,V>.subMap
:
SortedMap<K,V> subMap(K fromKey, K toKey)
: Returns a view of the portion of this map whose keys range fromfromKey
, inclusive, totoKey
, exclusive.
This inclusive lower bound, exclusive upper bound combo ("half-open range") is something that is prevalent in Java, and while it does have its benefits, it also has its quirks, as we shall soon see.
The following snippet illustrates a simple usage of subMap
:
static <K,V> SortedMap<K,V> someSortOfSortedMap() {
return Collections.synchronizedSortedMap(new TreeMap<K,V>());
}
//...
SortedMap<Integer,String> map = someSortOfSortedMap();
map.put(1, "One");
map.put(3, "Three");
map.put(5, "Five");
map.put(7, "Seven");
map.put(9, "Nine");
System.out.println(map.subMap(0, 4));
// prints "{1=One, 3=Three}"
System.out.println(map.subMap(3, 7));
// prints "{3=Three, 5=Five}"
The last line is important: 7=Seven
is excluded, due to the exclusive upper bound nature of subMap
. Now suppose that we actually need an inclusive upper bound, then we could try to write a utility method like this:
static <V> SortedMap<Integer,V>
subMapInclusive(SortedMap<Integer,V> map, int from, int to) {
return (to == Integer.MAX_VALUE)
? map.tailMap(from)
: map.subMap(from, to + 1);
}
Then, continuing on with the above snippet, we get:
System.out.println(subMapInclusive(map, 3, 7));
// prints "{3=Three, 5=Five, 7=Seven}"
map.put(Integer.MAX_VALUE, "Infinity");
System.out.println(subMapInclusive(map, 5, Integer.MAX_VALUE));
// {5=Five, 7=Seven, 9=Nine, 2147483647=Infinity}
A couple of key observations need to be made:
subMapInclusive
assumes Integer
keys for to + 1
to work.
Long
keys is not possible (see related questions)Long
, we need to compare against Long.MAX_VALUE
insteadByte
, Character
, etc, as keys, must all be written individuallytoInclusive == Integer.MAX_VALUE
, because +1
would overflow, and subMap
would throw IllegalArgumentException: fromKey > toKey
String
keys? Or some unknown type that may not even be Comparable<?>
?So the question is: is it possible to write a general subMapInclusive
method that takes a SortedMap<K,V>
, and K fromKey, K toKey
, and perform an inclusive-range subMap
queries?
NavigableMap
It should be mentioned that there's a NavigableMap.subMap
overload that takes two additional boolean
variables to signify whether the bounds are inclusive or exclusive. Had this been made available in SortedMap
, then none of the above would've even been asked.
So working with a NavigableMap<K,V>
for inclusive range queries would've been ideal, but while Collections
provides utility methods for SortedMap
(among other things), we aren't afforded the same luxury with NavigableMap
.
NavigableMap
Here is my implementation for a general inclusive submap. Here I am assuming that since the maps are sorted the time complexity of tailmap will be low, so the trick is to start with the tail and look at the keys returned, and then based on those keys either take a tail, the regular submap, or the submap with the next key:
static <K, V> SortedMap<K,V>
subMapInclusive(SortedMap<K,V> map, K from, K to) {
if(to == null) return map.tailMap(from);
//What appears at key "to" or later?
Iterator<K> keys = map.tailMap(to).keySet().iterator();
//Nothing, just take tail map.
if(!keys.hasNext()) return map.tailMap(from);
K key = keys.next();
//The first item found isn't to so regular submap will work
if(!to.equals(key)) return map.subMap(from, to);
//to is in the map
//it is not the last key
if(keys.hasNext()) return map.subMap(from, keys.next());
//it is the last key
return map.tailMap(from);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With