How to Use Guava's Range Class for Interval Operations in Java

Guava's immutable Range<C> class represents intervals of comparable values using two bounds—lower and upper—that can be open, closed, or unbounded, providing methods for membership testing, intersection, and spanning.

The Range class in Google's Guava library (google/guava) provides a robust, immutable implementation of mathematical intervals for Java applications. Unlike standard Java collections, Guava Range handles continuous or discrete value ranges with precise boundary semantics, making it ideal for validating inputs, querying date ranges, or implementing scheduling logic. The class is implemented in guava/src/com/google/common/collect/Range.java and is built atop the internal Cut<C> abstraction.

Creating Ranges with Static Factory Methods

The public API of Range consists of static factory methods that create the nine basic range types by assembling appropriate Cut instances. These methods are located in guava/src/com/google/common/collect/Range.java between lines 45 and 82.

Basic Range Types

You can construct ranges using intuitive factory methods that specify whether each bound is inclusive (closed) or exclusive (open):

  • Range.open(lower, upper): Creates an exclusive range (lower..upper) using Cut.aboveValue(lower) and Cut.belowValue(upper).
  • Range.closed(lower, upper): Creates an inclusive range [lower..upper].
  • Range.closedOpen(lower, upper): Creates [lower..upper).
  • Range.openClosed(lower, upper): Creates (lower..upper].
  • Range.singleton(value): Creates a closed range containing exactly one value.
  • Range.all(): Creates an unbounded range (-∞..+∞).
Range<Integer> open = Range.open(1, 5);          // (1..5)
Range<Integer> closed = Range.closed(1, 5);      // [1..5]
Range<Integer> closedOpen = Range.closedOpen(1, 5); // [1..5)
Range<Integer> all = Range.all();               // (-∞..+∞)

Custom Bounds with BoundType

For dynamic bound configuration, use the generic range(lower, lowerType, upper, upperType) factory with the BoundType enum defined in guava/src/com/google/common/collect/BoundType.java. This enum specifies whether each endpoint is OPEN or CLOSED:

Range<Integer> custom = Range.range(1, BoundType.CLOSED, 10, BoundType.OPEN); // [1..10)

The Cut Abstraction and Internal Architecture

In guava/src/com/google/common/collect/Range.java, the Range<C> class stores two Cut<C> objects: lowerBound and upperBound. The Cut hierarchy—defined in guava/src/com/google/common/collect/Cut.java—encodes the four possible physical states of a bound (below all, above all, below a specific value, above a specific value) and supplies the comparison logic used by all range operations.

This architecture allows Range to handle complex boundary semantics consistently across all operations without requiring separate Interval classes.

Testing Membership and Set Operations

Range implements Predicate<C> (and Java 8's java.util.function.Predicate<C>), allowing it to be used wherever a predicate is required. The contains method at lines 103-110 in Range.java provides the canonical membership test.

Membership Testing

boolean contains3 = closed.contains(3);          // true
boolean contains5 = closedOpen.contains(5);      // false

Composite Operations

The class provides mathematical set operations defined in terms of the underlying Cut ordering:

  • intersection(Range other) (lines 135-156): Returns the maximal range enclosed by both ranges.
  • span(Range other) (lines 106-122): Returns the minimal range that encloses both ranges.
  • encloses(Range other): Returns true if the bounds of the other range do not extend outside this range's bounds.
  • isConnected(Range other): Returns true if there exists a range that encloses both ranges.
Range<Integer> r1 = Range.closed(1, 4);   // [1..4]
Range<Integer> r2 = Range.closed(3, 6);   // [3..6]
Range<Integer> intersect = r1.intersection(r2); // [3..4]
Range<Integer> span = r1.span(r2);               // [1..6]
boolean encloses = r1.encloses(Range.singleton(2)); // true

Enumerating Discrete Values with ContiguousSet

Range does not iterate over the values it contains. To enumerate discrete values (such as integers), combine a Range with a DiscreteDomain via ContiguousSet.create():

ImmutableSortedSet<Integer> ints = ContiguousSet.create(
        Range.closed(1, 5), DiscreteDomain.integers()); // {1,2,3,4,5}

Summary

  • Immutable architecture: Range<C> uses two Cut<C> objects to represent bounds, ensuring thread safety and consistent behavior across all operations.
  • Flexible construction: Use static factories like open(), closed(), and range() with BoundType to create the nine basic interval types.
  • Predicate support: Range implements Predicate<C>, enabling functional-style membership testing with the contains() method.
  • Set operations: Perform mathematical operations including intersection(), span(), and encloses() with guaranteed correctness for all bound combinations.
  • Discrete enumeration: Use ContiguousSet.create() with a DiscreteDomain to iterate over integer ranges; Range itself does not support iteration.
  • Validation: Empty ranges like [a..a) are allowed, but invalid ranges like (a..a) throw IllegalArgumentException during construction.

Frequently Asked Questions

What is the difference between open and closed bounds in Guava Range?

Open bounds (exclusive) exclude the endpoint value, denoted by parentheses in mathematical notation (a..b), while closed bounds (inclusive) include the endpoint, denoted by brackets [a..b]. In Guava, Range.open(1, 5) contains 2, 3, and 4 but not 1 or 5, whereas Range.closed(1, 5) contains all integers from 1 through 5. The BoundType enum in guava/src/com/google/common/collect/BoundType.java explicitly defines these as OPEN and CLOSED.

How do I check if two ranges overlap or connect?

Use the isConnected() method to determine if there exists any value (or range) that could connect two ranges, or use intersection() to get the overlapping portion. If ranges are not connected, intersection() throws an IllegalArgumentException. For example, Range.closed(1, 4).isConnected(Range.closed(3, 6)) returns true because the ranges share values 3 and 4.

Can I use Guava Range with custom comparable types?

Yes, Range<C> requires type parameter C to extend Comparable<C>. Any class implementing Comparable can be used with Range, including custom domain objects, dates, or timestamps. The underlying Cut abstraction in guava/src/com/google/common/collect/Cut.java handles the comparison logic using the type's natural ordering.

Why does creating a range throw an IllegalArgumentException?

Guava validates range boundaries during construction to ensure the lower bound does not exceed the upper bound. Ranges like (a..a) or (5..3) are considered empty or invalid and throw IllegalArgumentException. However, certain empty ranges like [a..a) (closed-open with same value) are explicitly allowed as valid empty intervals.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →