1. Overview
In this tutorial, we’ll show how to use the Google Guava’s RangeMap interface and its implementations.
A RangeMap is a special kind of mapping from disjoint non-empty ranges to non-null values. Using queries, we may look up the value for any particular range in that map.
The basic implementation of RangeMap is a TreeRangeMap. Internally the map makes use of a TreeMap to store the key as a range and the value as any custom Java object.
2. Google Guava’s RangeMap
Let’s have a look at how to use the RangeMap class.
2.1. Maven Dependency
Let’s start by adding Google’s Guava library dependency in the pom.xml:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>21.0</version> </dependency>
The latest version of the dependency can be checked here.
3. Creating
Some of the ways in which we may create an instance of RangeMap are:
- Use the create method from the TreeRangeMap class to create a mutable map:
RangeMap<Integer, String> experienceRangeDesignationMap = TreeRangeMap.create();
- If we intend to create an immutable range map, use the ImmutableRangeMap class (which follows a builder pattern):
RangeMap<Integer, String> experienceRangeDesignationMap = new ImmutableRangeMap.<Integer, String>builder() .put(Range.closed(0, 2), "Associate") .build();
4. Using
Let’s start with a simple example showing the usage of RangeMap.
4.1. Retrieval Based on Input Within a Range
We can get a value associated with a value within a range of integers:
@Test public void givenRangeMap_whenQueryWithinRange_returnsSucessfully() { RangeMap<Integer, String> experienceRangeDesignationMap = TreeRangeMap.create(); experienceRangeDesignationMap.put( Range.closed(0, 2), "Associate"); experienceRangeDesignationMap.put( Range.closed(3, 5), "Senior Associate"); experienceRangeDesignationMap.put( Range.closed(6, 8), "Vice President"); experienceRangeDesignationMap.put( Range.closed(9, 15), "Executive Director"); assertEquals("Vice President", experienceRangeDesignationMap.get(6)); assertEquals("Executive Director", experienceRangeDesignationMap.get(15)); }
Note:
- The closed method of the Range class assumes the range of integer values to be between 0 to 2 (both inclusive)
- The Range in the above example consists of integers. We may use a range of any type, as long as it implements the Comparable interface such as String, Character, floating point decimals etc.
- RangeMap returns Null when we try to get the value for a range that is not present in map
- In a case of an ImmutableRangeMap, a range of one key cannot overlap with a range of a key that needs to be inserted. If that happens, we get an IllegalArgumentException
- Both keys and values in the RangeMap cannot be null. If either one of them is null, we get a NullPointerException
4.2. Removing a Value Based on a Range
Let’s see how we can remove values. In this example, we show how to remove a value associated with an entire range. We also show how to remove a value based on a partial key range:
@Test public void givenRangeMap_whenRemoveRangeIsCalled_removesSucessfully() { RangeMap<Integer, String> experienceRangeDesignationMap = TreeRangeMap.create(); experienceRangeDesignationMap.put( Range.closed(0, 2), "Associate"); experienceRangeDesignationMap.put( Range.closed(3, 5), "Senior Associate"); experienceRangeDesignationMap.put( Range.closed(6, 8), "Vice President"); experienceRangeDesignationMap.put( Range.closed(9, 15), "Executive Director"); experienceRangeDesignationMap.remove(Range.closed(9, 15)); experienceRangeDesignationMap.remove(Range.closed(1, 4)); assertNull(experienceRangeDesignationMap.get(9)); assertEquals("Associate", experienceRangeDesignationMap.get(0)); assertEquals("Senior Associate", experienceRangeDesignationMap.get(5)); assertNull(experienceRangeDesignationMap.get(1)); }
As can be seen, even after partially removing values from a range, we still can get the values if the range is still valid.
4.3. Span of Key Range
In case we would like to know what the overall span of a RangeMap is, we may use the span method:
@Test public void givenRangeMap_whenSpanIsCalled_returnsSucessfully() { RangeMap<Integer, String> experienceRangeDesignationMap = TreeRangeMap.create(); experienceRangeDesignationMap.put( Range.closed(0, 2), "Associate"); experienceRangeDesignationMap.put( Range.closed(3, 5), "Senior Associate"); experienceRangeDesignationMap.put( Range.closed(6, 8), "Vice President"); experienceRangeDesignationMap.put( Range.closed(9, 15), "Executive Director"); experienceRangeDesignationMap.put( assertEquals(0, experienceSpan.lowerEndpoint().intValue()); assertEquals(15, experienceSpan.upperEndpoint().intValue()); }
4.4. Getting a SubRangeMap
When we want to select a part from a RangeMap, we may use the subRangeMap method:
@Test public void givenRangeMap_whenSpanIsCalled_returnsSucessfully() { RangeMap<Integer, String> experienceRangeDesignationMap = TreeRangeMap.create(); experienceRangeDesignationMap.put( Range.closed(0, 2), "Associate"); experienceRangeDesignationMap.put( Range.closed(3, 5), "Senior Associate"); experienceRangeDesignationMap.put( Range.closed(6, 8), "Vice President"); experienceRangeDesignationMap.put( Range.closed(9, 15), "Executive Director"); RangeMap<Integer, String> experiencedSubRangeDesignationMap = experienceRangeDesignationMap.subRangeMap(Range.closed(4, 14)); assertNull(experiencedSubRangeDesignationMap.get(3)); assertEquals("Executive Director", experiencedSubRangeDesignationMap.get(14)); assertEquals("Vice President", experiencedSubRangeDesignationMap.get(7)); }
4.5. Getting an Entry
Finally, if we are looking for an Entry from a RangeMap, we use the getEntry method:
@Test public void givenRangeMap_whenGetEntryIsCalled_returnsEntrySucessfully() { RangeMap<Integer, String> experienceRangeDesignationMap = TreeRangeMap.create(); experienceRangeDesignationMap.put( Range.closed(0, 2), "Associate"); experienceRangeDesignationMap.put( Range.closed(3, 5), "Senior Associate"); experienceRangeDesignationMap.put( Range.closed(6, 8), "Vice President"); experienceRangeDesignationMap.put( Range.closed(9, 15), "Executive Director"); Map.Entry<Range<Integer>, String> experienceEntry = experienceRangeDesignationMap.getEntry(10); assertEquals(Range.closed(9, 15), experienceEntry.getKey()); assertEquals("Executive Director", experienceEntry.getValue()); }
5. Conclusion
In this tutorial, we illustrated examples of using the RangeMap in the Guava library. It is predominantly used to get a value based on the key specified as a from the map.
The implementation of these examples can be found in the GitHub project – this is a Maven-based project, so it should be easy to import and run as is.