Why this matters: the design in lesson 2 stands or falls on a handful of lines — the overlap comparison, and the floor/ceiling check against a sorted map. Interviewers watch whether you can produce exactly those lines cleanly, in the order a live round demands: vocabulary types first, the ordered structure next, then the two flows that matter. Everything here is written the way you'd write it on a shared editor, narration included.
Vocabulary types first
Start with the words the rest of the code will speak. Ten lines, and every later signature gets sharper:
enum Equipment { SCREEN, VIDEO_CONF, WHITEBOARD }
record TimeSlot(Instant start, Instant end) {
// Half-open: [start, end). Back-to-back slots do not overlap.
boolean overlaps(TimeSlot other) {
return start.isBefore(other.end) && other.start.isBefore(end);
}
}
record Room(String id, int capacity, Set<Equipment> equipment) {}
record Booking(String id, String roomId, String organizer,
int attendees, TimeSlot slot) {}Say the comment out loud as you type it: "half-open, so ten-to-eleven and eleven-to-twelve don't clash." The overlaps method is the only place in the program that knows the comparison — that was invariant #2, and here it is as code.
The calendar: one room's ordered truth
class RoomCalendar {
// start time -> booking; sorted order is the whole point
private final TreeMap<Instant, Booking> byStart = new TreeMap<>();
boolean isFree(TimeSlot candidate) {
// Only the neighbors can clash: the booking starting just
// before the candidate, and the one starting at/after it.
var floor = byStart.floorEntry(candidate.start());
if (floor != null && floor.getValue().slot().overlaps(candidate))
return false;
var ceiling = byStart.ceilingEntry(candidate.start());
if (ceiling != null && ceiling.getValue().slot().overlaps(candidate))
return false;
return true;
}
void add(Booking b) { // caller has already passed isFree
byStart.put(b.slot().start(), b);
}
void remove(Booking b) {
byStart.remove(b.slot().start());
}
}Narrate the floor/ceiling reasoning as you write it — it's the round's centerpiece: "the floor entry is the last meeting to start before mine; if it doesn't run into me, nothing earlier can, because they start even sooner and bookings in this map never overlap each other. The ceiling entry is the next to start; if I don't run into it, I can't reach anything later." Two probes into a sorted map, each O(log n), and the scan is gone.
The scheduler: flows over the calendar
class Scheduler {
private final Map<String, Room> rooms; // roomId -> Room
private final Map<String, RoomCalendar> calendars; // roomId -> calendar
private final Map<String, Booking> bookingsById; // for cancel
private final RankingStrategy ranking; // smallest-fit today
Booking book(String roomId, String organizer, int attendees,
Set<Equipment> needed, TimeSlot slot) {
Room room = rooms.get(roomId);
require(room.capacity() >= attendees, "room too small");
require(room.equipment().containsAll(needed), "missing equipment");
RoomCalendar cal = calendars.get(roomId);
require(cal.isFree(slot), "slot conflicts with an existing booking");
Booking b = new Booking(newId(), roomId, organizer, attendees, slot);
cal.add(b);
bookingsById.put(b.id(), b);
return b;
}
void cancel(String bookingId) {
Booking b = bookingsById.remove(bookingId);
require(b != null, "no such booking");
calendars.get(b.roomId()).remove(b);
// Slot is genuinely gone: every future isFree() is already correct.
}
}Point at cancel and say it: removal, not a flag — no future code path has to remember cancelled bookings exist.
The finder: filter, then rank
interface RankingStrategy {
Optional<Room> pick(List<Room> candidates);
}
class SmallestFit implements RankingStrategy {
public Optional<Room> pick(List<Room> candidates) {
return candidates.stream()
.min(Comparator.comparingInt(Room::capacity));
}
}
Optional<Room> suggest(int attendees, Set<Equipment> needed, TimeSlot slot) {
List<Room> fits = rooms.values().stream()
.filter(r -> r.capacity() >= attendees)
.filter(r -> r.equipment().containsAll(needed))
.filter(r -> calendars.get(r.id()).isFree(slot))
.toList();
return ranking.pick(fits);
}Two things to say while writing this. First: the availability filter calls the same isFree that book uses — one authority, no second opinion. Second: the filters are plain code because they're requirements, and the rank is a strategy because it's preference — the seam sits exactly where change is expected.
What you'd say about complexity
Conflict check: two TreeMap probes, O(log n) in bookings per room — thousands per room per year means about a dozen comparisons. Booking and cancellation: the same O(log n) plus hash-map work. Suggestion: O(R log n) for R rooms, since each surviving room pays one availability check — fine at hundreds of rooms; and say the honest caveat that if suggestion became the dominant operation, you'd want an index over rooms by capacity to shrink R before the per-room checks.
Key takeaway
Write it in this order: the half-open TimeSlot with its single overlaps line, the TreeMap calendar whose floor/ceiling probes replace the scan, then book and cancel through the calendar and suggest through filter-then-rank. Narrate the neighbor argument while typing it — "only the bookings adjacent in start order can clash, because the map never holds overlaps" — that sentence, plus the code matching it, is the round's core evidence.