Functional Scope (In-Scope)
- Constant-time O(1) Operations: Supports adding, removing, and re-linking list nodes in constant time using combined DLL and Map indexing.
- Repeat Playback State Machine: Handles standard traversal variants (None, Single-track repeat, Full-list wrapping repeat) smoothly.
- Stateless Shuffle Controller: Integrates shuffle mode using pointer indices, allowing quick toggle actions without corrupting the base DLL hierarchy.
- O(1) Track Movement API: Re-links pointers between target and destination nodes instantly to handle drag-and-drop operations efficiently.
Explicit Boundaries (Out-of-Scope)
- Concrete Core Audio Decoders: Omits real-time audio playback engines, network stream buffers, and codecs.
- GUI Elements & Waveform Graphics: Simulates active track indices instead of writing browser rendering code, player control interfaces, or volume sliders.
Production reference implementations demonstrating doubly linked lists, O(1) removals, re-linking, and playlist traversal states in Java and Python:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
class Song {
private final String id;
private final String title;
private final String artist;
private final int durationSeconds;
public Song(String id, String title, String artist, int durationSeconds) {
this.id = id;
this.title = title;
this.artist = artist;
this.durationSeconds = durationSeconds;
}
public String getId() { return id; }
public String getTitle() { return title; }
public String getArtist() { return artist; }
public int getDurationSeconds() { return durationSeconds; }
@Override
public String toString() {
return title + " - " + artist;
}
}
class PlaylistNode {
Song song;
PlaylistNode prev;
PlaylistNode next;
public PlaylistNode(Song song) {
this.song = song;
}
}
enum RepeatMode {
NONE, ONE, ALL
}
enum ShuffleMode {
OFF, SHUFFLE
}
class PlaylistManager {
private PlaylistNode head;
private PlaylistNode tail;
private PlaylistNode current;
private final Map<String, PlaylistNode> nodeMap = new HashMap<>();
private final ReentrantLock lock = new ReentrantLock();
private RepeatMode repeatMode = RepeatMode.NONE;
private ShuffleMode shuffleMode = ShuffleMode.OFF;
private final List<String> shuffleOrder = new ArrayList<>();
private int shuffleIndex = -1;
// O(1) Add to end
public void addSong(Song song) {
lock.lock();
try {
if (nodeMap.containsKey(song.getId())) {
throw new IllegalArgumentException("Song already exists in playlist.");
}
PlaylistNode newNode = new PlaylistNode(song);
nodeMap.put(song.getId(), newNode);
if (head == null) {
head = newNode;
tail = newNode;
current = newNode; // Set pointer if first song
} else {
tail.next = newNode;
newNode.prev = tail;
tail = newNode;
}
if (shuffleMode == ShuffleMode.SHUFFLE) {
shuffleOrder.add(song.getId());
}
} finally {
lock.unlock();
}
}
// O(1) Delete node
public void removeSong(String songId) {
lock.lock();
try {
PlaylistNode node = nodeMap.get(songId);
if (node == null) return;
// If removing the currently playing node, advance current pointer
if (node == current) {
current = getNextNodeLogical();
}
// Unlink node
if (node.prev != null) {
node.prev.next = node.next;
} else {
head = node.next; // Head removed
}
if (node.next != null) {
node.next.prev = node.prev;
} else {
tail = node.prev; // Tail removed
}
nodeMap.remove(songId);
shuffleOrder.remove(songId);
// Adjust shuffle index
if (shuffleMode == ShuffleMode.SHUFFLE) {
if (shuffleOrder.isEmpty()) {
shuffleIndex = -1;
current = null;
} else {
shuffleIndex = Math.min(shuffleIndex, shuffleOrder.size() - 1);
}
}
} finally {
lock.unlock();
}
}
// O(1) Move node to a different position
public void moveBefore(String songIdToMove, String targetSongId) {
lock.lock();
try {
if (songIdToMove.equals(targetSongId)) return;
PlaylistNode toMove = nodeMap.get(songIdToMove);
PlaylistNode target = nodeMap.get(targetSongId);
if (toMove == null || target == null) {
throw new IllegalArgumentException("Song node parameters not found.");
}
// 1. Unlink toMove from its current position
if (toMove.prev != null) {
toMove.prev.next = toMove.next;
} else {
head = toMove.next;
}
if (toMove.next != null) {
toMove.next.prev = toMove.prev;
} else {
tail = toMove.prev;
}
// 2. Link toMove before target
PlaylistNode prevOfTarget = target.prev;
toMove.next = target;
target.prev = toMove;
toMove.prev = prevOfTarget;
if (prevOfTarget != null) {
prevOfTarget.next = toMove;
} else {
head = toMove; // target was head
}
} finally {
lock.unlock();
}
}
public Song getCurrentlyPlaying() {
lock.lock();
try {
return (current != null) ? current.song : null;
} finally {
lock.unlock();
}
}
// Next track based on Repeat and Shuffle states
public Song next() {
lock.lock();
try {
if (current == null) return null;
if (repeatMode == RepeatMode.ONE) {
return current.song; // Repeat one: stay on current
}
PlaylistNode nextNode = getNextNodeLogical();
if (nextNode != null) {
current = nextNode;
return current.song;
}
current = null; // Playlist ended
return null;
} finally {
lock.unlock();
}
}
// Previous track
public Song prev() {
lock.lock();
try {
if (current == null) return null;
if (repeatMode == RepeatMode.ONE) {
return current.song;
}
PlaylistNode prevNode = getPrevNodeLogical();
if (prevNode != null) {
current = prevNode;
return current.song;
}
current = null;
return null;
} finally {
lock.unlock();
}
}
public void setRepeatMode(RepeatMode mode) {
lock.lock();
try {
this.repeatMode = mode;
} finally {
lock.unlock();
}
}
public void toggleShuffle() {
lock.lock();
try {
if (shuffleMode == ShuffleMode.OFF) {
shuffleMode = ShuffleMode.SHUFFLE;
generateShuffleOrder();
} else {
shuffleMode = ShuffleMode.OFF;
shuffleOrder.clear();
shuffleIndex = -1;
}
} finally {
lock.unlock();
}
}
private PlaylistNode getNextNodeLogical() {
if (shuffleMode == ShuffleMode.SHUFFLE) {
if (shuffleOrder.isEmpty()) return null;
shuffleIndex++;
if (shuffleIndex >= shuffleOrder.size()) {
if (repeatMode == RepeatMode.ALL) {
shuffleIndex = 0; // Wrap around permutation
} else {
return null; // Stop
}
}
return nodeMap.get(shuffleOrder.get(shuffleIndex));
} else {
if (current.next != null) {
return current.next;
} else if (repeatMode == RepeatMode.ALL) {
return head; // Wrap around DLL
}
return null;
}
}
private PlaylistNode getPrevNodeLogical() {
if (shuffleMode == ShuffleMode.SHUFFLE) {
if (shuffleOrder.isEmpty()) return null;
shuffleIndex--;
if (shuffleIndex < 0) {
if (repeatMode == RepeatMode.ALL) {
shuffleIndex = shuffleOrder.size() - 1;
} else {
return null;
}
}
return nodeMap.get(shuffleOrder.get(shuffleIndex));
} else {
if (current.prev != null) {
return current.prev;
} else if (repeatMode == RepeatMode.ALL) {
return tail;
}
return null;
}
}
private void generateShuffleOrder() {
shuffleOrder.clear();
shuffleOrder.addAll(nodeMap.keySet());
Collections.shuffle(shuffleOrder);
if (current != null) {
shuffleIndex = shuffleOrder.indexOf(current.song.getId());
} else {
shuffleIndex = 0;
}
}
public List<Song> getOrderedPlaylist() {
lock.lock();
try {
List<Song> songs = new ArrayList<>();
PlaylistNode runner = head;
while (runner != null) {
songs.add(runner.song);
runner = runner.next;
}
return songs;
} finally {
lock.unlock();
}
}
}
public class Main {
public static void main(String[] args) {
System.out.println("=== JAVA MUSIC PLAYLIST DEMO ===");
PlaylistManager manager = new PlaylistManager();
Song s1 = new Song("1", "Bohemian Rhapsody", "Queen", 354);
Song s2 = new Song("2", "Hotel California", "Eagles", 390);
Song s3 = new Song("3", "Stairway to Heaven", "Led Zeppelin", 482);
Song s4 = new Song("4", "Imagine", "John Lennon", 183);
manager.addSong(s1);
manager.addSong(s2);
manager.addSong(s3);
manager.addSong(s4);
System.out.println("Playlist Order: " + manager.getOrderedPlaylist());
System.out.println("Currently playing: " + manager.getCurrentlyPlaying());
System.out.println("Skip next: " + manager.next());
System.out.println("Skip next: " + manager.next());
System.out.println("Moving Imagine (s4) before Bohemian Rhapsody (s1)...");
manager.moveBefore("4", "1");
System.out.println("New Playlist Order: " + manager.getOrderedPlaylist());
System.out.println("Setting Repeat ALL and skipping next...");
manager.setRepeatMode(RepeatMode.ALL);
System.out.println("Skip next: " + manager.next());
System.out.println("Skip next: " + manager.next());
System.out.println("Toggling Shuffle...");
manager.toggleShuffle();
System.out.println("Skip next in shuffle: " + manager.next());
System.out.println("Removing Stairway to Heaven (s3)...");
manager.removeSong("3");
System.out.println("New Playlist Order: " + manager.getOrderedPlaylist());
System.out.println("=== END OF DEMO ===");
}
}