bartuomiei
bartuomiei

Reputation: 45

Object + List of Objects in a List?

I've got an interesting and troublesome task to solve. I need to create a playlist (some kind of list) that contains songs and other sub-playlists of songs... Each playlist has a playing mode (random, sequence, etc.) Is it possible to create such playlist? I thought about cracking the sub-playlists and adding extraxted songs from it to the master playlist or creating a sub-playlist for every song that is added to master playlist (I don't really like this idea) Somehow it gets around the problem however it is necessary to remain playing mode of each playlist...

For example:

Master playlist(sequence palymode) has: (1) song1-1/ (2) subplaylist(song2-1, song2-2, song-2-3) with random playmode/ (3) song1-2

The desired outcome: (1) song1-1/ (2) song2-3 (starting random subplaylist)/ (3) song2-1/ (4) song2-2/ (5) song1-2/

how should I approach this?

Upvotes: 1

Views: 320

Answers (3)

MusicMan
MusicMan

Reputation: 960

Approach 1: Create a class named playlist and playlist item which can hold list of songIds, which can hold set of songs from different playlists or song ids.

class PlayList{
  List<PlayListItem> playlistItems;
}

class PlayListItem{
  List<String> songIds;
}

This helps if a particular if you want to identify the set of songs added via a particular sub playlist. However this approach makes the iteration little difficult compared to approach 2

Approach 2: Here the list is avoided in playlist item, so that iteration while displaying playlist is simple. However to identify the list of songIds that was added via a particularSubPlaylist has to be computed.

class PlayList{
  List<PlayListItem> playlistItems;
}
class PlayListItem{
  String songId;
  String referencePlayListId;
}

Upvotes: 0

Glains
Glains

Reputation: 2863

Altough there are already some answers, i promised to provide a sample implementation. Starting of we have a common interface Playable which is the class to be implemented for the composite design pattern.

public interface Playable {
    String getSongName();
}

Next, the Song class to represent a single song.

public class Song implements Playable {

    private String name;

    public Song(String name) {
        this.name = name;
    }

    @Override
    public String getSongName() {
        return name;
    }
}

In preparation for the Playlist class an enum to represent the difference playing modes.

public enum PlayingMode {
    SEQUENCE, RANDOM
}

Now, finally the playlist class.

public class Playlist implements Playable {

    private String name;
    private List<Playable> playables = new ArrayList<>();
    private PlayingMode mode;

    private Playable currentItem;
    private List<Playable> next = new ArrayList<>();

    public Playlist(String name, PlayingMode mode) {
        this.name = name;
        this.mode = mode;
    }

    @Override
    public String getSongName() {
        if (playables.isEmpty()) {
            return null;
        }

        if (currentItem == null) {
            // initialize the playing songs
            next.addAll(playables);
            if (mode == PlayingMode.RANDOM) {
                Collections.shuffle(next);
            }
            currentItem = next.get(0);
        } else {
            // if we have a playlist, play its songs first
            if (currentItem instanceof Playlist) {
                String candidate = currentItem.getSongName();
                if (candidate != null) {
                    return candidate;
                }
            }

            int index = next.indexOf(currentItem);
            index++;
            if (index < next.size()) {
                currentItem = next.get(index);
            } else {
                currentItem = null;
            }
        }

        return currentItem != null ? currentItem.getSongName() : null;
    }

    private void addToNext(Playable playable) {
        if (currentItem == null) {
            return;
        }

        // if the playlist is playing, add it to those list as well
        if (mode == PlayingMode.SEQUENCE) {
            next.add(playable);
        } else if (mode == PlayingMode.RANDOM) {
            int currentIndex = next.indexOf(currentItem);
            int random = ThreadLocalRandom.current().nextInt(currentIndex, next.size());
            next.add(random, playable);
        }
    }

    public void addPlayable(Playable playable) {
        Objects.requireNonNull(playable);
        playables.add(playable);
        addToNext(playable);
    }
}

Some examples:

public static void main(String[] args) {
    Song song1 = new Song("Song 1");
    Song song2 = new Song("Song 2");

    Playlist subPlaylist1 = new Playlist("Playlist 1", PlayingMode.RANDOM);
    subPlaylist1.addPlayable(new Song("Song A"));
    subPlaylist1.addPlayable(new Song("Song B"));
    subPlaylist1.addPlayable(new Song("Song C"));

    Song song3 = new Song("Song 3");

    Playlist main = new Playlist("Main", PlayingMode.SEQUENCE);
    main.addPlayable(song1);
    main.addPlayable(song2);
    main.addPlayable(subPlaylist1);
    main.addPlayable(song3);

    String songName = main.getSongName();
    while (songName != null) {
        System.out.println("Current song is: " + songName);
        songName = main.getSongName();

    }
}

Could give the output:

Current song is: Song 1
Current song is: Song 2
Current song is: Song B
Current song is: Song A
Current song is: Song C
Current song is: Song 3

You can also add songs while playing:

while (songName != null) {
    System.out.println("Current song is: " + songName);
    songName = main.getSongName();

    // add songs while playing
    if ("Song A".equals(songName)) {
        subPlaylist1.addPlayable(new Song("Song D"));
        subPlaylist1.addPlayable(new Song("Song E"));
        subPlaylist1.addPlayable(new Song("Song F"));
    }
}

This could lead to:

Current song is: Song 1
Current song is: Song 2
Current song is: Song B
Current song is: Song A
Current song is: Song E
Current song is: Song D
Current song is: Song F
Current song is: Song C
Current song is: Song 3

Some final notes:

  • The getIndex method does have a worst case runtime of O(n), which can be an issue if there are many songs in the playlist. A faster Collection like Set or Map will give better performace, but the implementation is a bit more complex.
  • The classes have been simplified, which means some getters and setters as well as equals and hashCode have been omitted for brevity.

Upvotes: 1

ariganis
ariganis

Reputation: 298

Since I suspect that this is some sort of homework, I will only provide you with a partial implementation, so you get an idea how to proceed.

Create an abstract class PlaylistElement, which can later either be a Song or another Playlist.

abstract class PlaylistElement {
    public abstract List<Song> printSongs();
}

Implement a class Playlist extending PlaylistElement.

class Playlist extends PlaylistElement {
    private List<PlaylistElement> elements;
    private PlaybackMode playbackMode;
    @Override
    public List<Song> printSongs() {
        if(this.playbackMode == PlaybackMode.RANDOM) {
            List<Song> songs = new ArrayList<>();
            List<PlaylistElement> shuffleElements = new ArrayList<>();

            //Add all PlaylistElements from elements into shuffleElements
            //and shuffle the shuffleElements collection

            //insert your songs into the songs collection here by sequentially
            //going through your
            //PlaylistElements and inserting the result of their printSongs()
            //implementation (e.g. in a for-loop)

            return songs;
        }
        else if(this.playbackMode == PlaybackMode.SEQUENTIAL) {
            //you can do this on your own
        }
        return null;
    }
}

Implement a class Song extending PlaylistElement.

class Song extends PlaylistElement {
    private String title;
    private String artist;
    .
    .
    .
    @Override
    public List<Song> printSongs() {
        //return a List with only this Song instance inside
        return Arrays.asList(new Song[] { this });
    }
}

Create an enum for your Playlist Playback Modes.

enum PlaybackMode {
    SEQUENTIAL, RANDOM;
}

Hope this gives you a general idea! Getters/Setters and other important parts omitted for brevity.

Upvotes: 3

Related Questions