Start learning free

LESSON 564 OF 569 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Playlist in Java: Two Classes Working Together

Practice O(n) Read the full explanation

A music app keeps playlists: songs in the order they were added. Build it from two classes that work together. A Song holds one song's data. A Playlist keeps a list of Song objects and answers questions about them.

Implement:

  • Song(title, seconds) holds a song's title and its length in seconds;
  • Playlist() starts with no songs. Its methods:
    • add(song) adds a Song at the end of the playlist;
    • remove(title) removes the first song with that title and returns true; if no song has that title, it changes nothing and returns false;
    • totalSeconds() returns the sum of the songs' lengths in seconds (0 for an empty playlist);
    • duration() returns the same total as a string "m:ss": the minutes, a colon, then the seconds with two digits. 5 seconds are "0:05", 750 seconds are "12:30", and 3600 seconds are "60:00": the minutes are never padded.

The starter code spells every name the way your language does.

The input is a list of operations, applied in order to one new playlist. The output lists what the remove, totalSeconds and duration calls return, in order, with booleans printed as True and False.

 

Example 1:

Input:
    add(Song("Intro", 65))
    add(Song("Sunrise", 200))
    totalSeconds()
    duration()
    remove("Intro")
    duration()
Output: 265 4:25 True 3:20
Explanation: The two songs last 65 + 200 = 265 seconds: 4 minutes (240 seconds) and 25 seconds.
After "Intro" is removed, only the 200 seconds of "Sunrise" are left: 3 minutes and 20 seconds.

Example 2:

Input:
    duration()
    remove("Rain")
    totalSeconds()
Output: 0:00 False 0
Explanation: The playlist is empty: it lasts 0 seconds, and there is no "Rain" to remove.

Example 3:

Input:
    add(Song("Echo", 30))
    add(Song("Echo", 45))
    add(Song("Rain", 5))
    remove("Echo")
    totalSeconds()
    remove("Echo")
    remove("Echo")
    duration()
Output: True 50 True False 0:05
Explanation: Two songs are called "Echo". The first remove takes only the first one, the 30 seconds,
so 45 + 5 = 50 seconds are left. The second remove takes the other "Echo", and the third finds none.
Only "Rain" is left: 5 seconds, written 0:05.

 

Constraints:

  • 1 <= seconds <= 3600
  • A title is one word of letters and digits. Titles are compared exactly: "Rain" and "rain" are different titles.
  • At most 200 operations.
STEP 1 OF 16
    Ctrl + Enter to check

    JAVA · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Playlist in Java, explained

    Problem overview

    A playlist keeps songs in the order they were added. You build it from two classes that work together:

    • Song(title, seconds) holds one song's title and length in seconds.
    • Playlist() starts with no songs and has four methods:
      • add(song) adds a Song at the end.
      • remove(title) removes the first song with that title and returns true. If no song has that title, it changes nothing and returns false.
      • totalSeconds() returns the sum of the lengths (0 for an empty playlist).
      • duration() returns the same total as "m:ss": the minutes, a colon, and the seconds with two digits. 5 seconds are "0:05", 750 seconds are "12:30" and 3600 seconds are "60:00". The minutes are never padded.

    In the first example, a playlist gets Song("Intro", 65) and Song("Sunrise", 200). totalSeconds() returns 265 and duration() returns 4:25, since 265 seconds are 4 minutes (240 seconds) and 25 seconds. After remove("Intro") returns true, only 200 seconds are left and duration() returns 3:20. The lesson's tests print booleans as True and False.

    Approach: a class that owns a list

    Song is plain data: two public fields set by a constructor. Playlist owns a List<Song> in a private field, so only its own methods can change the list. The rest of the program has to go through add and remove, and the numbers that totalSeconds() and duration() report can't get out of step with the real songs.

    The methods each need a short loop over the songs:

    • remove must work by position, so it uses an index loop, songs.get(i) and songs.remove(i).
    • totalSeconds only reads every song, so it uses a for-each loop and a running total.
    • duration doesn't loop at all: it calls totalSeconds() and formats the result.

    Step by step

    1. Song's fields and constructor. Declare public String title; and public int seconds;, then store the parameters with this.title = title; and this.seconds = seconds;.
    2. The playlist's list. Declare private List<Song> songs = new ArrayList<>();. A new playlist starts empty.
    3. add. Add the song to the end of the list. The list keeps the Song object itself.
    4. remove. Loop with an index i from 0 to songs.size() - 1. When songs.get(i).title.equals(title), call songs.remove(i) and return true right away. After the loop, return false.
    5. totalSeconds. Start total at 0, add each song.seconds, and return the total.
    6. duration. Get the total, then minutes = total / 60 and seconds = total % 60. If seconds is below 10, return minutes + ":0" + seconds. Otherwise return minutes + ":" + seconds.

    The solution in Java

    class Song {
        public String title;
        public int seconds;
    
        public Song(String title, int seconds) {
            this.title = title;
            this.seconds = seconds;
        }
    }
    
    class Playlist {
        private List<Song> songs = new ArrayList<>();
    
        public void add(Song song) {
            songs.add(song);
        }
    
        public boolean remove(String title) {
            for (int i = 0; i < songs.size(); i++) {
                if (songs.get(i).title.equals(title)) {
                    songs.remove(i);
                    return true;
                }
            }
            return false;
        }
    
        public int totalSeconds() {
            int total = 0;
            for (Song song : songs) {
                total += song.seconds;
            }
            return total;
        }
    
        public String duration() {
            int total = totalSeconds();
            int minutes = total / 60;
            int seconds = total % 60;
            if (seconds < 10) {
                return minutes + ":0" + seconds;
            }
            return minutes + ":" + seconds;
        }
    }

    Details worth knowing

    • Compare titles with equals. == on strings compares references and can be false for two titles with the same letters. equals compares the characters, so "Rain" and "rain" are different titles.
    • remove(i) removes by position. On a List<Song>, remove(int) takes out the element at that index, and the later elements move up by one place. That is safe here because the method returns right after, and never uses the shifted index.
    • Returning early removes only the first match. With two songs called Echo, the first remove("Echo") takes the first one and stops.
    • Strings and +. In minutes + ":0" + seconds, the first operand is an int but the second is a String, so + joins the pieces as text. The result is "0:05" for 5 seconds.
    • Integer division and remainder. total / 60 is the whole minutes and total % 60 is what is left. For 265 seconds, that is 4 and 25.

    Using the classes

    Two songs share the title Echo. Each remove takes out the first one it finds, so the duplicates are removed one at a time, in the order they were added:

    Playlist playlist = new Playlist();
    playlist.add(new Song("Echo", 30));
    playlist.add(new Song("Echo", 45));
    playlist.add(new Song("Rain", 5));
    System.out.println(playlist.remove("Echo"));
    System.out.println(playlist.totalSeconds());
    System.out.println(playlist.remove("Echo"));
    System.out.println(playlist.remove("Echo"));
    System.out.println(playlist.duration());
    
    true
    50
    true
    false
    0:05
    

    The first call removes the 30-second song, leaving 45 + 5 = 50 seconds. The second removes the 45-second song, and the third finds no Echo, so it returns false and changes nothing. Only Rain is left, and 5 seconds are written 0:05. In a file of your own, this code also needs import java.util.*;.

    Complexity

    With n songs, add takes constant time, remove takes O(n) (a search, then a shift of the later songs), totalSeconds takes O(n), and duration takes O(n) because it calls totalSeconds. The playlist uses O(n) space for its songs. With at most 200 operations, none of this matters for speed.

    Testing

    Run on small playlists, the methods return:

    CallsResults
    add Intro 65, add Sunrise 200, totalSeconds(), duration(), remove("Intro"), duration()265, 4:25, true, 3:20
    On an empty playlist: duration(), remove("Rain"), totalSeconds()0:00, false, 0
    add Echo 30, add Echo 45, add Rain 5, then remove("Echo"), totalSeconds(), remove("Echo"), remove("Echo"), duration()true, 50, true, false, 0:05
    One song of 750 seconds: duration()12:30
    One song of 3600 seconds: duration()60:00
    Rain 5, then remove("rain") and duration()false, 0:05

    Common mistakes

    • Forgetting the leading zero. Returning minutes + ":" + seconds for every case prints 0:5 for 5 seconds.
    • Padding the minutes. 3600 seconds are 60:00, not 1:00:00, and 5 seconds are 0:05, not 00:05.
    • Not returning after the removal. If the loop keeps going after songs.remove(i), it removes every song with that title and skips the one that moves into index i.
    • Returning true or false too early. return false belongs after the loop. Inside the loop it would stop at the first song that doesn't match.
    • Using == for titles. Use equals.
    • Keeping a separate total. A running total field can drift out of step with the list after a removal. Computing the total from the list each time is simpler and always right.
    Keep going with Java500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free