Back to Blog
Java

Java TreeSet ceiling and floor methods

java treeset ceiling floor: Learn how to use TreeSet's ceiling and floor methods to find nearest elements, handle edge cases, and understand their performance.

TreeSetJava CollectionsSortedSetNavigableSetJava API
Illustration of a TreeSet with ceiling and floor pointers finding nearest values in a sorted set

java treeset ceiling floor requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When you need to find the smallest element in a sorted set that is greater than or equal to a given value, or the largest element that is less than or equal to it, TreeSet's ceiling and floor methods provide a direct, efficient answer. These methods are part of the NavigableSet interface, which TreeSet implements, and they turn a common search problem into a single method call.

Why Use ceiling and floor on a TreeSet

A TreeSet maintains its elements in sorted order, either by their natural ordering or by a Comparator provided at creation time. This sorted structure makes it possible to answer questions like "what is the next available slot after this number?" or "which item is closest to a given threshold?" without manually iterating through the collection. The ceiling and floor methods are designed exactly for these scenarios, giving you a direct lookup instead of writing a loop that scans the entire set.

Consider a scheduling system that tracks occupied time slots as integers. To find the first free slot after a requested time, you can use ceiling. To find the last occupied slot before a deadline, you can use floor. Without these methods, you would need to either maintain a separate sorted array and perform a binary search or iterate over the set and compare each element, both of which are more verbose and error-prone.

How ceiling and floor Work

The ceiling method takes a single argument and returns the least element in the set that is greater than or equal to that argument. The floor method returns the greatest element that is less than or equal to the argument. If no such element exists, both methods return null. The argument does not need to be present in the set; it is only used as a comparison point.

import java.util.TreeSet; public class CeilingFloorExample { public static void main(String[] args) { TreeSet<Integer> numbers = new TreeSet<>(); numbers.add(10); numbers.add(20); numbers.add(30); numbers.add(40); System.out.println(numbers.ceiling(25)); // 30 System.out.println(numbers.floor(25)); // 20 System.out.println(numbers.ceiling(45)); // null System.out.println(numbers.floor(5)); // null } }

In this example, the set contains 10, 20, 30, and 40. Calling ceiling(25) returns 30 because it is the smallest element greater than or equal to 25. floor(25) returns 20 because it is the largest element less than or equal to 25. When the argument is outside the set's range, the methods return null.

The return type matches the type of the elements stored in the TreeSet. If the set contains custom objects, the comparison is performed using the set's comparator or the objects' natural ordering, so the same rules apply.

Code Examples: Finding the Nearest Value

A common use case is finding the closest value to a given number. You can combine ceiling and floor to determine which is nearer. The following example demonstrates this for a set of integers.

import java.util.TreeSet; public class NearestValue { public static Integer findNearest(TreeSet<Integer> values, int target) { Integer floor = values.floor(target); Integer ceiling = values.ceiling(target); if (floor == null) { return ceiling; // no element below or equal } if (ceiling == null) { return floor; // no element above or equal } int floorDist = target - floor; int ceilingDist = ceiling - target; return (floorDist <= ceilingDist) ? floor : ceiling; } public static void main(String[] args) { TreeSet<Integer> numbers = new TreeSet<>(); numbers.add(10); numbers.add(25); numbers.add(40); System.out.println(findNearest(numbers, 20)); // 25 System.out.println(findNearest(numbers, 30)); // 25 System.out.println(findNearest(numbers, 50)); // 40 } }

This method returns the element with the smallest absolute difference from the target. When both floor and ceiling are available, it compares the distances and picks the closer one. If the target is less than the smallest element, only ceiling is non-null, so it returns that. Conversely, if the target is greater than the largest element, only floor is non-null. This pattern works because TreeSet is sorted and both methods run in logarithmic time.

Handling Edge Cases and Null Values

TreeSet does not allow null elements, but the argument passed to ceiling and floor can be null. If you pass null to a TreeSet that uses natural ordering, a NullPointerException is thrown because the set cannot compare null to its elements. If the set uses a Comparator that accepts null, the behavior depends on that comparator. In practice, you should avoid passing null to these methods unless you have explicitly designed your comparator to handle it.

Another edge case is an empty TreeSet. Both ceiling and floor return null for any argument, because there are no elements to return. This is consistent with the method contract and does not throw an exception.

When the argument exactly matches an element in the set, both ceiling and floor return that element. For example, if the set contains 20 and you call ceiling(20), the result is 20. The same applies to floor(20). This is a key difference from the higher and lower methods, which exclude the argument itself and return strictly greater or strictly smaller elements.

Performance Characteristics of ceiling and floor

TreeSet is backed by a TreeMap, which is a red-black tree. The ceiling and floor methods perform a tree traversal to locate the appropriate node, so their time complexity is O(log n), where n is the number of elements in the set. This is significantly faster than a linear scan, which would be O(n), and it makes these methods suitable for large sets.

The logarithmic cost applies to the lookup itself. Adding and removing elements also take O(log n) time, so the overall cost of maintaining the set and performing navigation queries remains predictable. If you need to perform many nearest-value lookups on a static collection, a TreeSet is a reasonable choice. If the collection changes frequently, the overhead of maintaining the tree is still acceptable for most applications.

It is worth noting that these methods do not modify the set. They are read-only operations, so they can be called concurrently without synchronization as long as the set itself is not being structurally modified. However, TreeSet is not thread-safe. If multiple threads access a TreeSet and at least one thread modifies it, you must synchronize externally or use a concurrent sorted set implementation.

Choosing Between ceiling, floor, higher, and lower

The NavigableSet interface defines four related methods: ceiling, floor, higher, and lower. The difference lies in whether they include the argument value itself. Ceiling and floor are inclusive: they return the argument if it is present in the set. Higher and lower are exclusive: they return the smallest element strictly greater than the argument, or the largest element strictly less than it.

MethodReturnsInclusive of argument?
ceiling(e)least element >= eYes
floor(e)greatest element <= eYes
higher(e)least element > eNo
lower(e)greatest element < eNo

Choose ceiling or floor when you want to include the exact value if it exists. For example, when checking if a specific key is present and also want the next one, ceiling is convenient. Use higher or lower when you need to skip over the exact match, such as finding the next strictly greater element in a range query.

Consider a set of version numbers. If you want to find the latest version that is at least a given release, ceiling is appropriate. If you need the next version after a specific release, higher is the right choice. Understanding this distinction prevents off-by-one errors in boundary conditions.

Practical Use Cases for Navigation Methods

These methods are useful in many real-world scenarios beyond simple nearest-value lookups. One common pattern is implementing a range query: given a start and end value, you can use ceiling(start) to get the first element in the range and then iterate until you exceed the end. Another pattern is finding the predecessor or successor in a sorted set, which is useful for algorithms that process intervals or maintain a sorted list of events.

In a caching system, you might use floor to find the most recent cache entry before a timestamp. In a network scheduler, ceiling can find the next available time slot. The methods also work well with custom objects. For instance, a TreeSet of Event objects sorted by time can use a comparator that compares only the timestamp field, allowing you to call ceiling with a dummy event containing the desired time.

import java.util.TreeSet; class Event implements Comparable<Event> { int time; String name; Event(int time, String name) { this.time = time; this.name = name; } @Override public int compareTo(Event other) { return Integer.compare(this.time, other.time); } } public class EventScheduler { public static void main(String[] args) { TreeSet<Event> events = new TreeSet<>(); events.add(new Event(10, "Start")); events.add(new Event(20, "Mid")); events.add(new Event(30, "End")); Event query = new Event(15, ""); Event next = events.ceiling(query); System.out.println(next.name); // Mid } }

In this example, the comparator compares only the time field, so the name field is ignored. The ceiling method finds the first event with a time greater than or equal to 15. This pattern works because TreeSet uses the comparator consistently for all operations, including insertion and lookup.

When using custom objects, ensure that the comparator is consistent with equals, or at least that the navigation methods behave as expected. If the comparator does not match equals, a TreeSet may contain distinct objects that compare as equal, and ceiling or floor might return one of them arbitrarily. This is a general contract issue with TreeSet, not specific to these methods.

For most applications, ceiling and floor provide a clean, efficient way to navigate a sorted set. They are part of the standard Java Collections Framework and are available in all modern Java versions. By understanding their behavior, edge cases, and performance, you can use them confidently in your own code.

java treeset ceiling floor: Practical Usage and Code Example | RYUSLOG DEV