← Bytedance Interview Insights
This is already a hard problem on its own and adding TTL made it significantly more involved.
Start by clarifying requirements: TTL per entry, eviction policy (LRU), and expected operations. Then design a data structure combining a hash map for O(1) access and a doubly linked list for LRU order, with lazy expiration on access and optional background cleanup. Discuss trade-offs between eager vs lazy expiration and how to handle expired entries during get and put.
Pro tip: Mention that you would use lazy expiration to avoid overhead, but also consider a background thread for proactive cleanup to prevent memory bloat—showing awareness of real-world production concerns. Also, highlight that TTL should be checked on both get and put to ensure expired entries are not returned or counted towards capacity.
Ask about expected operations (get, put), TTL granularity, concurrency needs, and whether TTL is set per entry or globally. Confirm that expired entries should be treated as non-existent.
Use a hash map for O(1) key lookup and a doubly linked list to maintain LRU order. Each node stores key, value, and expiration timestamp.
On get, check if the entry exists and if it's expired. If expired, remove it and return null; otherwise, move it to the front (most recently used) and return the value.
On put, if key exists, update value and expiration, and move to front. If new, check capacity: evict least recently used (and expired entries if any) before inserting. Set expiration timestamp based on TTL.
Explain lazy expiration vs. background cleanup. Lazy is simpler but may leave expired entries until accessed; background cleanup prevents memory bloat but adds complexity. Mention thread-safety if needed.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.