Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
263: NavigableMap and NavigableSet
Imagine you're managing a massive physical archive of records, organized strictly by date. If I ask you for the record from exactly January 1st, 2023, and it's not there, a standard sorted map would just tell you "not found." But in the real world, you'd probably say, "I don't have the 1st, but I have the 31st of December—that's the closest one before it—and the 2nd of January, which is the closest one after."
That's exactly what NavigableMap and NavigableSet bring to the table. They take the sorting we already have in SortedMap and SortedSet and add "navigation" capabilities. Instead of just checking if a key exists, you can now ask for the "nearest neighbor" of a key, or grab a slice of the collection that falls between two specific points.
Finding the Nearest Neighbor
I've seen a lot of developers try to find the "closest" key by iterating through the whole map with a loop. Please, don't do that. It's inefficient and clutters your code. NavigableMap gives us four specific methods to handle this. Let's look at them using a tax bracket example, where the key is the income threshold and the value is the tax rate.
NavigableMap<Integer, Double> taxBrackets = new TreeMap<>();
taxBrackets.put(0, 0.10);
taxBrackets.put(11000, 0.12);
taxBrackets.put(44725, 0.22);
taxBrackets.put(95375, 0.24);
// I want to know which bracket a person making $50,000 falls into.
// floorKey returns the greatest key less than or equal to the given key.
Integer bracket = taxBrackets.floorKey(50000);
System.out.println("Your bracket starts at: " + bracket); // Output: 44725
// ceilingKey returns the smallest key greater than or equal to the given key.
Integer nextBracket = taxBrackets.ceilingKey(50000);
System.out.println("The next bracket starts at: " + nextBracket); // Output: 95375
The distinction between floor/ceiling and lower/higher is subtle but important: floor and ceiling are inclusive (they return the key itself if it matches exactly), while lower and higher are strictly exclusive. If you're looking for "the one right after this, regardless of whether this one exists," use higherKey.
Slicing the Map and Set
Sometimes you don't just want one key; you want a range. You might want every record from the start of Q1 to the end of Q2. NavigableMap allows you to create "views" of your data. These aren't copies of the map—they are windows into the original data. If you change the original map, the view changes too.
// Get everything from $11,000 up to (but not including) $95,375
SortedMap<Integer, Double> middleClass = taxBrackets.subMap(11000, 95375);
// Get everything from $44,725 to the very end of the map
SortedMap<Integer, Double> highEarners = taxBrackets.tailMap(44725);
The same logic applies to NavigableSet. If you have a set of available appointment timestamps, you can use ceiling() to find the first available slot after the current time, or descendingSet() to flip the entire set and process your appointments in reverse order without having to manually sort a list.
The Convenience of Polling
One of my favorite additions in the NavigableMap interface is the "poll" methods. In a standard Map, removing the first or last element is a bit of a chore—you have to find the key first, then call remove(). pollFirstEntry() and pollLastEntry() do both in one shot.
This is incredibly useful when you're implementing something like a priority queue or a task scheduler where you always want to process the highest (or lowest) priority item and immediately remove it from the pending list so it doesn't get processed twice.
📋 Practical Task
Build a Shipping Weight Tier Lookup
You are building a shipping calculator for a logistics company. Shipping costs are based on weight tiers. If a package's weight falls between two tiers, it is charged the rate of the lower tier (the floor).
Requirements:
- Create a
NavigableMap<Double, Double>(Weight in kg $\rightarrow$ Price in USD). - Populate it with these tiers:
- 0.0 kg: $5.00
- 1.0 kg: $8.00
- 5.0 kg: $15.00
- 10.0 kg: $25.00
- 20.0 kg: $40.00
- Write a method
calculateShipping(double weight)that usesfloorEntry()to find the correct price for a given weight. - Test it with three weights:
0.5(should be $5.00),4.2(should be $8.00), and25.0(should be $40.00). - Bonus: Use
descendingMap()to print the tiers from heaviest to lightest.
There are no comments for now.