1. Overview
In this tutorial, we’ll show how to use the Google Guava’s RangeSet interface and its implementations.
A RangeSet is a set comprising of zero or more non-empty, disconnected ranges. When adding a range to a mutable RangeSet, any connected ranges are merged together while empty ranges are ignored.
The basic implementation of RangeSet is a TreeRangeSet.
2. Google Guava’s RangeSet
Let’s have a look at how to use the RangeSet 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. Creation
Let’s explore some of the ways in which we can create an instance of RangeSet.
First, we can use the create method from the class TreeRangeSet to create a mutable set:
RangeSet<Integer> numberRangeSet = TreeRangeSet.create();
If we already have collections in place, use the create method from the class TreeRangeSet to create a mutable set by passing that collection:
List<Range<Integer>> numberList = Arrays.asList(Range.closed(0, 2)); RangeSet<Integer> numberRangeSet = TreeRangeSet.create(numberList);
Finally, if we need to create an immutable range set, use the ImmutableRangeSet class (creating which follows a builder pattern):
RangeSet<Integer> numberRangeSet = new ImmutableRangeSet.<Integer>builder().add(Range.closed(0, 2)).build();
4. Usage
Let’s start with a simple example that shows the usage of RangeSet.
4.1. Adding to a Range
We can check whether the input supplied is within a range present in any of the range items in a set:
@Test public void givenRangeSet_whenQueryWithinRange_returnsSucessfully() { RangeSet<Integer> numberRangeSet = TreeRangeSet.create(); numberRangeSet.add(Range.closed(0, 2)); numberRangeSet.add(Range.closed(3, 5)); numberRangeSet.add(Range.closed(6, 8)); assertTrue(numberRangeSet.contains(1)); assertFalse(numberRangeSet.contains(9)); }
Notes:
- The closed method of the Range class assumes the range of integer values to be between 0 to 2 (both inclusive)
- The Range in above example consists of integers. We can use a range consisting of any type as long as it implements the Comparable interface such as String, Character, floating point decimals etc
- In the case of an ImmutableRangeSet, a range item present in the set cannot overlap with a range item that one would like to add. If that happens, we get an IllegalArgumentException
- Range input to a RangeSet cannot be null. If the input is null, we will get a NullPointerException
4.2. Removing a Range
Let’s see how we can remove values from a RangeSet:
@Test public void givenRangeSet_whenRemoveRangeIsCalled_removesSucessfully() { RangeSet<Integer> numberRangeSet = TreeRangeSet.create(); numberRangeSet.add(Range.closed(0, 2)); numberRangeSet.add(Range.closed(3, 5)); numberRangeSet.add(Range.closed(6, 8)); numberRangeSet.add(Range.closed(9, 15)); numberRangeSet.remove(Range.closed(3, 5)); numberRangeSet.remove(Range.closed(7, 10)); assertTrue(numberRangeSet.contains(1)); assertFalse(numberRangeSet.contains(9)); assertTrue(numberRangeSet.contains(12)); }
As can be seen, after removal we can still access values present in any of the range items left in the set.
4.3. Range Span
Let’s now see what the overall span of a RangeSet is:
@Test public void givenRangeSet_whenSpanIsCalled_returnsSucessfully() { RangeSet<Integer> numberRangeSet = TreeRangeSet.create(); numberRangeSet.add(Range.closed(0, 2)); numberRangeSet.add(Range.closed(3, 5)); numberRangeSet.add(Range.closed(6, 8)); Range<Integer> experienceSpan = numberRangeSet.span(); assertEquals(0, experienceSpan.lowerEndpoint().intValue()); assertEquals(8, experienceSpan.upperEndpoint().intValue()); }
4.4. Getting a Subrange
If we wish to get part of RangeSet based on a given Range, we can use the subRangeSet method:
@Test public void givenRangeSet_whenSubRangeSetIsCalled_returnsSubRangeSucessfully() { RangeSet<Integer> numberRangeSet = TreeRangeSet.create(); numberRangeSet.add(Range.closed(0, 2)); numberRangeSet.add(Range.closed(3, 5)); numberRangeSet.add(Range.closed(6, 8)); RangeSet<Integer> numberSubRangeSet = numberRangeSet.subRangeSet(Range.closed(4, 14)); assertFalse(numberSubRangeSet.contains(3)); assertFalse(numberSubRangeSet.contains(14)); assertTrue(numberSubRangeSet.contains(7)); }
4.5. Complement Method
Next, let’s get all the values except the one present in RangeSet, using the complement method:
@Test public void givenRangeSet_whenComplementIsCalled_returnsSucessfully() { RangeSet<Integer> numberRangeSet = TreeRangeSet.create(); numberRangeSet.add(Range.closed(0, 2)); numberRangeSet.add(Range.closed(3, 5)); numberRangeSet.add(Range.closed(6, 8)); RangeSet<Integer> numberRangeComplementSet = numberRangeSet.complement(); assertTrue(numberRangeComplementSet.contains(-1000)); assertFalse(numberRangeComplementSet.contains(2)); assertFalse(numberRangeComplementSet.contains(3)); assertTrue(numberRangeComplementSet.contains(1000)); }
4.6. Intersection with a Range
Finally, when we would like to check whether a range interval present in RangeSet intersects with some or all the values in another given range, we can make use of the intersect method:
@Test public void givenRangeSet_whenIntersectsWithinRange_returnsSucessfully() { RangeSet<Integer> numberRangeSet = TreeRangeSet.create(); numberRangeSet.add(Range.closed(0, 2)); numberRangeSet.add(Range.closed(3, 10)); numberRangeSet.add(Range.closed(15, 18)); assertTrue(numberRangeSet.intersects(Range.closed(4, 17))); assertFalse(numberRangeSet.intersects(Range.closed(19, 200))); }
5. Conclusion
In this tutorial, we illustrated the RangeSet of the Guava library using some examples. The RangeSet is predominantly used to check whether a value falls within a certain range present in the set.
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.