guava-rangeset
Guide to Guava RangeSet
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
2.1. Maven Dependency
<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
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
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
@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
@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
@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
@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.