Class IntervalSet<T extends Comparable<? super T>>
- Type Parameters:
T
- the type of interval boundaries, must implementComparable
- All Implemented Interfaces:
Iterable<IntervalSet.Interval<T>>
Core Capabilities
IntervalSet efficiently manages collections of intervals with the following key features:
- O(log n) performance - Uses
ConcurrentSkipListMap
for efficient lookups, insertions, and range queries - Thread-safe - Lock-free reads with minimal locking for writes only
- Auto-merging behavior - Overlapping intervals are automatically merged
- Intelligent interval splitting - Automatically splits intervals during removal operations
- Rich query API - Comprehensive set of methods for finding, filtering, and navigating intervals
- Type-safe boundaries - Supports precise boundary calculations for 20+ built-in types
Auto-Merging Behavior
Overlapping intervals are automatically merged into larger, non-overlapping intervals:
IntervalSet<Integer> set = new IntervalSet<>();
set.add(1, 5);
set.add(3, 8); // Merges with [1,5] to create [1,8]
set.add(10, 15); // Separate interval since no overlap
// Result: [1,8], [10,15]
Primary Client APIs
Basic Operations
add(T, T)
- Add an interval [start, end]remove(T, T)
- Remove an interval, splitting existing ones as neededremoveExact(T, T)
- Remove only exact interval matchesremoveRange(T, T)
- Remove a range, trimming overlapping intervalscontains(T)
- Test if a value falls within any intervalclear()
- Remove all intervals
Query and Navigation
intervalContaining(T)
- Find the interval containing a specific valuenextInterval(T)
- Find the next interval at or after a valuehigherInterval(T)
- Find the next interval strictly after a valuepreviousInterval(T)
- Find the previous interval at or before a valuelowerInterval(T)
- Find the previous interval strictly before a valuefirst()
/last()
- Get the first/last intervals
Bulk Operations and Iteration
iterator()
- Iterate intervals in ascending orderdescendingIterator()
- Iterate intervals in descending ordergetIntervalsInRange(T, T)
- Get intervals within a key rangegetIntervalsBefore(T)
- Get intervals before a keygetIntervalsFrom(T)
- Get intervals from a key onwardremoveIntervalsInKeyRange(T, T)
- Bulk removal by key range
Introspection and Utilities
size()
/isEmpty()
- Get count and emptiness statekeySet()
/descendingKeySet()
- Access start keys as NavigableSettotalDuration(java.util.function.BiFunction)
- Compute total duration across intervalssnapshot()
- Get atomic point-in-time copy of all intervals
Supported Types
IntervalSet provides intelligent boundary calculation for interval splitting/merging operations across a wide range of types:
- Numeric: Byte, Short, Integer, Long, Float, Double, BigInteger, BigDecimal
- Character: Character (Unicode-aware)
- Temporal: Date, java.sql.Date, Time, Timestamp, Instant, LocalDate, LocalTime, LocalDateTime, ZonedDateTime, OffsetDateTime, OffsetTime, Duration
- Custom: Any type implementing Comparable (with manual boundary handling if needed)
Thread Safety
IntervalSet is fully thread-safe with an optimized locking strategy:
- Lock-free reads: All query operations (contains, navigation, iteration) require no locking
- Minimal write locking: Only mutation operations acquire the internal ReentrantLock
- Weakly consistent iteration: Iterators don't throw ConcurrentModificationException
Common Use Cases
Time Range Management
IntervalSet<ZonedDateTime> schedule = new IntervalSet<>();
schedule.add(meeting1Start, meeting1End);
schedule.add(meeting2Start, meeting2End);
if (schedule.contains(proposedMeetingTime)) {
System.out.println("Time conflict detected");
}
Numeric Range Tracking
IntervalSet<Long> processedIds = new IntervalSet<>();
processedIds.add(1000L, 1999L); // First batch
processedIds.add(2000L, 2999L); // Second batch - automatically merges to [1000, 2999]
Duration totalWork = processedIds.totalDuration((start, end) ->
Duration.ofMillis(end - start + 1));
Performance Characteristics
All operations maintain O(log n) complexity:
- Add: O(log n) - May require merging adjacent intervals
- Remove/RemoveRange: O(log n) - May require splitting intervals
- Contains: O(log n) - Single floor lookup
- IntervalContaining: O(log n) - Single floor lookup
- Navigation: O(log n) - Leverages NavigableMap operations
- Iteration: O(n) - Direct map iteration, no additional overhead
- Author:
- John DeRegnaucourt ([email protected])
Copyright (c) Cedar Software LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
License
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - See Also:
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final class
IntervalSet.Interval<T extends Comparable<? super T>>
Immutable value object representing one interval. -
Constructor Summary
ConstructorsConstructorDescriptionCreates a new IntervalSet.IntervalSet
(IntervalSet<T> other) Copy constructor: creates a deep copy of the given IntervalSet, including intervals and custom functions.Creates a new IntervalSet with custom boundary functions.IntervalSet
(List<IntervalSet.Interval<T>> intervals) Creates a new IntervalSet from a list of intervals.IntervalSet
(List<IntervalSet.Interval<T>> intervals, Function<T, T> previousFunction, Function<T, T> nextFunction) Creates a new IntervalSet from a list of intervals with custom boundary functions. -
Method Summary
Modifier and TypeMethodDescriptionvoid
Add the inclusive interval [start,end].void
clear()
Remove all stored intervals from the set.boolean
True if the value lies in any closed interval [start,end].Returns an iterator over all intervals in descending order by start key.Returns a set of all start keys in descending order.difference
(IntervalSet<T> other) Returns a new IntervalSet that is the difference of this set minus the other.boolean
first()
Returns the first (lowest key) interval ornull
.getIntervalsBefore
(T toKey) Returns all intervals whose start keys are before the specified key.getIntervalsFrom
(T fromKey) Returns all intervals whose start keys are at or after the specified key.getIntervalsInRange
(T fromKey, T toKey) Returns all intervals whose start keys fall within the specified range [fromKey, toKey].int
hashCode()
higherInterval
(T value) Returns the next interval that starts strictly after the given value, ornull
if none exists.intersection
(IntervalSet<T> other) Returns a new IntervalSet that is the intersection of this set and the other.boolean
intersects
(IntervalSet<T> other) Returns true if this set intersects (overlaps) with the other set.intervalContaining
(T value) Return the interval covering the specifiedvalue
, ornull
if no interval contains it.boolean
isEmpty()
Returnstrue
if this set contains no intervals.iterator()
Returns an iterator over all stored intervals in ascending order by start key.keySet()
Returns a set of all start keys in the interval set.last()
Returns the last (highest key) interval ornull
.lowerInterval
(T value) Returns the previous interval that starts strictly before the given value, ornull
if none exists.nextInterval
(T value) Returns the next interval that contains the given value, or the next interval that starts after the value.previousInterval
(T value) Returns the previous interval that starts at or before the given value, ornull
if none exists.void
Remove the inclusive interval [start,end], splitting existing intervals as needed.boolean
removeExact
(T start, T end) Remove an exact interval [start, end] that matches a stored interval exactly.int
removeIntervalsInKeyRange
(T fromKey, T toKey) Removes all intervals whose start keys fall within the specified range [fromKey, toKey].void
removeRange
(T start, T end) Remove the inclusive range [start, end] from the set, trimming and splitting intervals as necessary.int
size()
Number of stored, non-overlapping intervals.snapshot()
Returns a snapshot copy of all intervals at the time of invocation.toString()
Compute the total covered duration across all stored intervals using a default mapping.totalDuration
(BiFunction<T, T, Duration> toDuration) Compute the total covered duration across all stored intervals.union
(IntervalSet<T> other) Returns a new IntervalSet that is the union of this set and the other.Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
Methods inherited from interface java.lang.Iterable
forEach, spliterator
-
Constructor Details
-
IntervalSet
public IntervalSet()Creates a new IntervalSet. Overlapping intervals will be automatically merged when added. -
IntervalSet
Creates a new IntervalSet with custom boundary functions.For custom types not supported by built-in previous/next logic, provide functions to compute the previous and next values. If null, falls back to built-in support. Users must provide these for non-built-in types to enable proper interval splitting during removals.
- Parameters:
previousFunction
- custom function to compute previous value, or null for built-innextFunction
- custom function to compute next value, or null for built-in
-
IntervalSet
Copy constructor: creates a deep copy of the given IntervalSet, including intervals and custom functions.- Parameters:
other
- the IntervalSet to copy
-
IntervalSet
Creates a new IntervalSet from a list of intervals.This constructor enables JSON deserialization by allowing reconstruction of an IntervalSet from a previously serialized list of intervals. The intervals are added in order, with automatic merging of overlapping intervals as per normal IntervalSet behavior.
This is typically used in conjunction with
snapshot()
for serialization workflows:// Serialize: get snapshot for JSON serialization List<Interval<T>> intervals = intervalSet.snapshot(); // ... serialize intervals to JSON ... // Deserialize: reconstruct from JSON-deserialized list IntervalSet<T> restored = new IntervalSet<>(intervals);
- Parameters:
intervals
- the list of intervals to populate this set with- Throws:
NullPointerException
- if intervals list or any interval is null
-
IntervalSet
public IntervalSet(List<IntervalSet.Interval<T>> intervals, Function<T, T> previousFunction, Function<T, T> nextFunction) Creates a new IntervalSet from a list of intervals with custom boundary functions.This constructor enables JSON deserialization with custom discrete type support by allowing reconstruction of an IntervalSet from a previously serialized list of intervals along with the original boundary functions. This is essential when the IntervalSet was used with discrete types that require custom previous/next functions.
This is typically used for discrete types not among the 20+ built-in types:
// Original IntervalSet with custom functions for discrete type Function<MyType, MyType> prev = myType -> myType.previous(); Function<MyType, MyType> next = myType -> myType.next(); IntervalSet<MyType> original = new IntervalSet<>(prev, next); // Serialize: get snapshot for JSON serialization List<Interval<MyType>> intervals = original.snapshot(); // ... serialize intervals and functions to JSON ... // Deserialize: reconstruct with original functions IntervalSet<MyType> restored = new IntervalSet<>(intervals, prev, next);
- Parameters:
intervals
- the list of intervals to populate this set withpreviousFunction
- custom function to compute previous value, or null for built-innextFunction
- custom function to compute next value, or null for built-in- Throws:
NullPointerException
- if intervals list or any interval is null
-
-
Method Details
-
add
Add the inclusive interval [start,end]. Both start and end are inclusive.Overlapping intervals are merged automatically. When merging, if an interval with the same start key already exists, a union is performed using the maximum end value of both intervals.
Examples:
- Adding [1,5] then [1,8] results in [1,8] (union of overlapping intervals)
- Adding [1,5] then [3,8] results in [1,8] (overlapping intervals merged)
- Adding [1,5] then [1,3] results in [1,5] (smaller interval absorbed)
-
remove
Remove the inclusive interval [start,end], splitting existing intervals as needed. Both start and end are treated as inclusive bounds.Overlapping intervals are split where needed.
-
removeExact
Remove an exact interval [start, end] that matches a stored interval exactly.This operation acts only on a single stored interval whose start and end exactly match the specified values. No other intervals are merged, split, or trimmed as a result of this call. To remove a sub-range or to split existing intervals, use
remove(T, T)
orremoveRange(T, T)
.Both
start
andend
are treated as inclusive bounds. If no matching interval exists, the set remains unchanged. This method is thread-safe: it acquires the internal lock to perform removal under concurrent access but does not affect merging or splitting logic.- Parameters:
start
- the inclusive start key of the interval to remove (must match exactly)end
- the inclusive end key of the interval to remove (must match exactly)- Returns:
true
if an interval with exactly this start and end was found and removed;false
otherwise (no change to the set)
-
removeRange
Remove the inclusive range [start, end] from the set, trimming and splitting intervals as necessary.Intervals are trimmed and split as needed. Any stored interval that overlaps the removal range:
- If an interval begins before
start
, its right boundary is trimmed tostart
. - If an interval ends after
end
, its left boundary is trimmed toend
. - If an interval fully contains
[start,end]
, it is split into two intervals: one covering[originalStart, start]
and one covering[end, originalEnd]
. - Intervals entirely within
[start,end]
are removed.
This operation is thread-safe: it acquires the internal write lock during mutation.
Performance: O(log n)
- Parameters:
start
- inclusive start of the range to removeend
- inclusive end of the range to remove- Throws:
IllegalArgumentException
- ifend < start
- If an interval begins before
-
contains
True if the value lies in any closed interval [start,end]. Both boundaries are inclusive.Performance: O(log n)
-
intervalContaining
Return the interval covering the specifiedvalue
, ornull
if no interval contains it.Intervals are closed and inclusive on both ends ([start, end]), so a value v is contained in an interval
if start <= v <= end
. This method performs a lock-free read viaConcurrentSkipListMap.floorEntry(Object)
and does not mutate the underlying set.Performance: O(log n)
- Parameters:
value
- the non-null value to locate within stored intervals- Returns:
- an
IntervalSet.Interval
whose start and end bracketvalue
, ornull
if none - Throws:
NullPointerException
- ifvalue
is null
-
first
Returns the first (lowest key) interval ornull
. -
last
Returns the last (highest key) interval ornull
. -
nextInterval
Returns the next interval that contains the given value, or the next interval that starts after the value.If the value falls within an existing interval, that interval is returned. Otherwise, returns the next interval that starts after the value. Uses
NavigableMap.floorEntry(Object)
andNavigableMap.ceilingEntry(Object)
for O(log n) performance.- Parameters:
value
- the value to search from- Returns:
- the interval containing the value or the next interval after it, or
null
if none
-
higherInterval
Returns the next interval that starts strictly after the given value, ornull
if none exists.This method uses
NavigableMap.higherEntry(Object)
for O(log n) performance.- Parameters:
value
- the value to search from (exclusive)- Returns:
- the next interval strictly after the value, or
null
if none
-
previousInterval
Returns the previous interval that starts at or before the given value, ornull
if none exists.This method uses
NavigableMap.floorEntry(Object)
for O(log n) performance. Note: This returns the interval by start key, not necessarily the interval containing the value. UseintervalContaining(T)
to find the interval that actually contains a value.- Parameters:
value
- the value to search from (inclusive)- Returns:
- the previous interval at or before the value, or
null
if none
-
lowerInterval
Returns the previous interval that starts strictly before the given value, ornull
if none exists.This method uses
NavigableMap.lowerEntry(Object)
for O(log n) performance.- Parameters:
value
- the value to search from (exclusive)- Returns:
- the previous interval strictly before the value, or
null
if none
-
getIntervalsInRange
Returns all intervals whose start keys fall within the specified range [fromKey, toKey].This method uses
NavigableMap.subMap(Object, boolean, Object, boolean)
for efficient range queries. The returned list is ordered by start key.- Parameters:
fromKey
- the start of the range (inclusive)toKey
- the end of the range (inclusive)- Returns:
- a list of intervals within the specified range, ordered by start key
- Throws:
IllegalArgumentException
- if fromKey > toKey
-
getIntervalsBefore
Returns all intervals whose start keys are before the specified key.This method uses
NavigableMap.headMap(Object, boolean)
for efficient queries.- Parameters:
toKey
- the upper bound (exclusive)- Returns:
- a list of intervals before the specified key, ordered by start key
-
getIntervalsFrom
Returns all intervals whose start keys are at or after the specified key.This method uses
NavigableMap.tailMap(Object, boolean)
for efficient queries.- Parameters:
fromKey
- the lower bound (inclusive)- Returns:
- a list of intervals at or after the specified key, ordered by start key
-
descendingIterator
Returns an iterator over all intervals in descending order by start key.This method uses
NavigableMap.descendingMap()
for efficient reverse iteration. Like the standard iterator, this is weakly consistent and lock-free.- Returns:
- an iterator over intervals in descending order by start key
-
keySet
Returns a set of all start keys in the interval set.This method uses
NavigableMap.navigableKeySet()
to provide efficient key operations. The returned set supports range operations and is backed by the interval set.- Returns:
- a navigable set of start keys
-
descendingKeySet
Returns a set of all start keys in descending order.This method uses
NavigableMap.descendingKeySet()
for efficient reverse key iteration.- Returns:
- a navigable set of start keys in descending order
-
removeIntervalsInKeyRange
Removes all intervals whose start keys fall within the specified range [fromKey, toKey].This method uses
NavigableMap.subMap(Object, boolean, Object, boolean)
for efficient bulk removal. This is more efficient than callingremoveExact(T, T)
multiple times.- Parameters:
fromKey
- the start of the range (inclusive)toKey
- the end of the range (inclusive)- Returns:
- the number of intervals removed
- Throws:
IllegalArgumentException
- if fromKey > toKey
-
size
public int size()Number of stored, non-overlapping intervals. -
isEmpty
public boolean isEmpty()Returnstrue
if this set contains no intervals.- Returns:
true
if this set contains no intervals
-
clear
public void clear() -
snapshot
Returns a snapshot copy of all intervals at the time of invocation.This method provides a consistent point-in-time view of all intervals by acquiring the internal write lock and creating a complete copy of the interval set. The returned list is completely independent of the original IntervalSet and will not reflect any subsequent modifications.
This is useful when you need a stable view of intervals that won't change during processing, such as for bulk operations, reporting, analysis, or when integrating with code that expects stable collections.
The returned list contains intervals in ascending order by start key, matching the iteration order of this IntervalSet.
Thread Safety: This is the only "read" method that acquires the internal write lock to ensure the atomicity of the snapshot operation. All other query operations (contains, navigation, iteration) are lock-free. This method locks as a convenience to provide a guaranteed atomic snapshot rather than requiring users to manage external synchronization themselves.
Performance: O(n) where n is the number of intervals. The method creates a new ArrayList with exact capacity and copies all interval objects.
Memory: The returned list and its interval objects are completely independent copies. Modifying the returned list or the original IntervalSet will not affect the other.
- Returns:
- a new list containing copies of all intervals at the time of invocation, ordered by start key. Never returns null; returns empty list if no intervals.
-
totalDuration
Compute the total covered duration across all stored intervals using a default mapping.This overload uses a default BiFunction based on the type of T: - If T is Temporal (and supports SECONDS unit, e.g., Instant, LocalDateTime, etc.), uses Duration.between(start, end). - If T is Number, computes (end.longValue() - start.longValue() + 1) and maps to Duration.ofNanos(diff) (arbitrary unit). - If T is Date (or subclasses), computes Duration.ofMillis(end.getTime() - start.getTime()). - If T is Character, computes (end - start + 1) and maps to Duration.ofNanos(diff) (arbitrary unit). - If T is Duration, computes end.minus(start). - Otherwise, throws UnsupportedOperationException.
For Temporal types like LocalDate that do not support SECONDS, this will throw DateTimeException. For custom or unsupported types, use the BiFunction overload. For numeric types and characters, the unit (nanos) is arbitrary; use custom BiFunction for specific units.
- Returns:
- the sum of all interval durations
- Throws:
UnsupportedOperationException
- if no default mapping for type TDateTimeException
- if Temporal type does not support Duration.betweenArithmeticException
- if numeric computation overflows long
-
totalDuration
Compute the total covered duration across all stored intervals.The caller provides a
toDuration
function that maps each interval's start and end values to aDuration
. This method sums those Durations over all intervals in key order. This method uses the underlying set's lock-free iterator and may reflect concurrent modifications made during iteration.- Parameters:
toDuration
- a function that converts an interval [start, end] to a Duration- Returns:
- the sum of all interval durations
-
iterator
Returns an iterator over all stored intervals in ascending order by start key.This iterator is weakly consistent and lock-free, meaning it reflects live changes to the IntervalSet as they occur during iteration. The iterator does not throw
ConcurrentModificationException
and does not require external synchronization for reading.The iterator reflects the state of the IntervalSet at the time of iteration and may see concurrent modifications.
- Specified by:
iterator
in interfaceIterable<T extends Comparable<? super T>>
- Returns:
- an iterator over the intervals in this set, ordered by start key
-
union
Returns a new IntervalSet that is the union of this set and the other.- Parameters:
other
- the other IntervalSet- Returns:
- a new IntervalSet containing all intervals from both
-
intersection
Returns a new IntervalSet that is the intersection of this set and the other.Computes overlapping parts of intervals.
- Parameters:
other
- the other IntervalSet- Returns:
- a new IntervalSet containing intersecting intervals
-
difference
Returns a new IntervalSet that is the difference of this set minus the other.Equivalent to removing all intervals from other from this set.
- Parameters:
other
- the other IntervalSet to subtract- Returns:
- a new IntervalSet with intervals from other removed
-
intersects
Returns true if this set intersects (overlaps) with the other set.This method efficiently determines if any interval in this set overlaps with any interval in the other set. Two intervals overlap if they share at least one common value. This method provides an optimized check that avoids computing the full intersection.
Performance: O(n + m) where n and m are the sizes of the two sets, using a two-pointer merge algorithm to detect overlap without building intermediate results.
Examples:
- [1,5] intersects with [3,8] → true (overlap: [3,5])
- [1,5] intersects with [6,10] → false (no overlap)
- [1,5] intersects with [5,10] → true (overlap at single point: 5)
- Parameters:
other
- the other IntervalSet to check for overlap- Returns:
- true if there is any overlap between intervals in this and other sets
- Throws:
NullPointerException
- if other is null
-
equals
-
hashCode
public int hashCode() -
toString
-