Is there a way to get a tailMap or a headMap from an immutable Guava sorted multimap?

Viewed 81

There are a couple of ways in Guava to create a sorted immutable multiMap. One way is to first create a ListMultiMap with the keys as a tree and then turn it into an immutable multiMap.

Using a ListMultiMap we can have duplicate values for the same key:

ListMultimap<Integer, String> mutableMap = MultimapBuilder.treeKeys().arrayListValues().build();
return ImmutableListMultimap.copyOf(mutableMap);

Using a TreeMap we won't have duplicate values for the same key:

TreeMultimap<Integer, String> mutableMap = TreeMultimap.create(Ordering.natural(), Ordering.arbitrary());
return ImmutableListMultimap.copyOf(mutableMap);

The problem is that in both cases the immutable interface is generic and doesn't give a way to access the keys in a sorted manner so that we could have e.g. tailMap and headMap which are found in Java's NavigationalMap.

Is there a way to have an immutable sorted multi map in Guava that gives access to tailMap and headMap? Or do I need to just manually build an ImmutableSortedMap<Integer, ImmutableList<String>> to get this functionality?

1 Answers

You can use your custom ListMultimap with treeKeys() and cast the resulting map — it's safe as guaranteed by Guava's contract:

Uses a naturally-ordered TreeMap to map keys to value collections. The collections returned by Multimap.keySet(), Multimap.keys(), and Multimap.asMap() will iterate through the keys in sorted order.

For all multimaps generated by the resulting builder, the Multimap.keySet() can be safely cast to a SortedSet, and the Multimap.asMap() can safely be cast to a SortedMap.

Example:

ListMultimap<Integer, String> multimap = MultimapBuilder.treeKeys().arrayListValues().build();
multimap.putAll(ImmutableMultimap.of(1, "one", 1, "uno", 2, "two", 42, "forty-two"));
// {1=[one, uno], 2=[two], 42=[forty-two]}

SortedMap<Integer, List<String>> sortedMap = (SortedMap<Integer, List<String>>) Multimaps.asMap(multimap); // safe, see javadoc
// `.asMap()` directly on `multimap` would give you SortedMap<Integer, Collection<String>>
// SortedMap<Integer, Collection<String>> sortedMap = (SortedMap<Integer, Collection<String>>) multimap.asMap();
SortedMap<Integer, List<String>> tailMap = sortedMap.tailMap(2);
// {2=[two], 42=[forty-two]}
Related