Capstone: A Music Library
Bring lists, sets, maps, comparators, and immutability together in one multi-file challenge
We've covered the whole map: the four collection families, generic types, equality and hashing, comparators, iteration, immutability, and architecture. This chapter is one substantial challenge that exercises all of it.
You'll build a small MusicLibrary that ingests tracks and answers four queries:
byArtist(name)— all tracks for an artist, in insertion ordergenres()— the set of all unique genrestopRated(n)— the topntracks by rating (ties broken by title)albumsByArtist(name)— the unique album names for an artist, in insertion order
Each query corresponds directly to a collection shape we've studied:
| Query | Shape |
|---|---|
byArtist(name) | Map<String, List<Track>> |
genres() | Set<String> (insertion-ordered if we want a deterministic preview) |
topRated(n) | List<Track> sorted with a Comparator |
albumsByArtist(name) | Map<String, Set<String>> (insertion-ordered set) |
What "good" looks like
A clean implementation will:
- Store
Tracks in the shape that matches each query (not a flat list scanned every time). - Use
computeIfAbsentto build per-artist lists and sets lazily. - Use a
LinkedHashSetwhere insertion-ordered uniqueness matters. - Build
topRatedwith aComparator.comparingInt(...).reversed().thenComparing(...)chain. - Return read-only / snapshot collections from every getter.
The challenge
Implement MusicLibrary so that the driver in Main prints exactly:
Debussy tracks:
Clair de Lune
Reverie
La Mer
Genres: [classical, impressionist, orchestral]
Top 3:
Clair de Lune (5)
La Mer (5)
Liebestraum (4)
Debussy albums: [Suite Bergamasque, Reveries, Sea Pieces]
Hints:
computeIfAbsentfor "map of list" and "map of set" insertion.- For
Genres:use aLinkedHashSet<String>so iteration order is deterministic (insertion order). - For
topRated, sort a copy of the tracks list withComparator.comparingInt(Track::rating).reversed().thenComparing(Track::title)and take the firstn. - For
albumsByArtist, useLinkedHashSet<String>per artist. - Return safe views from every public method (
Collections.unmodifiableList/Collections.unmodifiableSet).
What you just demonstrated
That single class touches every major idea in the course:
- Right shape per query — one map of lists, one map of sets, one set, one list (Data Modeling).
computeIfAbsentfor "map of \*" — concise, race-free against itself (Maps).LinkedHashSet— uniqueness and insertion order (Sets).Comparatorcomposition —reversed().thenComparing(...)to combine two ordering criteria (Comparable & Comparator).- Safe getters — every returned collection is an unmodifiable view (Immutable Collections + Architecture).
record-backed value objects forTrack— typed data, freeequals/hashCode/toString(Equality and Hashing).
Test your understanding
Why LinkedHashSet<String> for genres() rather than HashSet<String>?
It's faster
It allows duplicates
It keeps insertion order, which makes the printed output deterministic and matches the user-visible "as encountered" order
It supports concurrent reads
In topRated, why sort a copy of the tracks list?
It is faster
Because sorting in place would permanently reorder the library, making "insertion order" queries (like byArtist) wrong on subsequent calls
The Comparator requires a fresh list
The original list is unmodifiable