I wrote a very simple to get program, to refresh my skills on LinkedLists in case I need them. Now, I stumbled on behaviour I did not expect. When I reach the end/beginning of my list and go forward/Backward, the same Element is displayed twice.
Here is how I thought it would work:
List = [<Pointer>E1, E2, E3]
after next() call:
List = [E1, <Pointer>E2, E3]
after next() call:
List = [E1, E2, <Pointer>E3]
after previous() call:
List = [E1, <Pointer>E2, E3]
but apparently, I have to call it twice to make the Pointer go back once. Why is this and how do I change that behaviour?
public class Playlist {
private LinkedList<Song> playList;
private ArrayList<Album> albums;
private Song currentSong;
Playlist() {
this.playList = new LinkedList<>();
this.albums = new ArrayList<>();
}
Playlist(ArrayList<Album> albums) {
this.playList = new LinkedList<>();
this.albums = albums;
}
void addAlbum(Album album) {
albums.add(album);
}
void addSong(Song s) {
if (albums.contains(s)) {
playList.add(s);
} else {
System.err.println("Song unknown");
}
}
public static void main(String[] args) {
Song s1 = new Song("song1", 111);
Song s2 = new Song("song2", 222);
Song s3 = new Song("song3", 333);
Song s4 = new Song("song4", 444);
Song s5 = new Song("song5", 555);
ArrayList<Song> songList1 = new ArrayList<>();
songList1.add(s1);
songList1.add(s2);
songList1.add(s3);
Album a1 = new Album(songList1);
ArrayList<Song> songList2 = new ArrayList<>();
songList2.add(s4);
songList2.add(s5);
Album a2 = new Album(songList2);
Playlist p1 = new Playlist();
p1.addAlbum(a1);
p1.addAlbum(a2);
for(Album a : p1.albums){
for(Song s : a.getSongs()){
p1.playList.add(s);
}
}
ListIterator<Song> listIterator = p1.playList.listIterator();
p1.currentSong = p1.playList.getFirst();
Scanner scanner = new Scanner(System.in);
p1.showMenu();
String input = scanner.nextLine();
;
while (!input.equals("q")) {
switch (input) {
case "s":
if ((listIterator.hasNext())) {
p1.currentSong = listIterator.next();
System.out.println("Current song: " + p1.currentSong);
} else {
System.out.println("End of playlist reached");
}
break;
case "p":
if (listIterator.hasPrevious()) {
p1.currentSong = listIterator.previous();
} else {
System.out.println("Start of playlist reached");
}
break;
default:
System.out.println("Invalid input.");
break;
}
p1.showMenu();
input = scanner.nextLine();
}
System.out.println("Goodbye");
scanner.close();
}
private void showMenu() {
System.out.print("This is the menu.\n Your options: (s) - skip the current song.\n (p) - play previous song. \n (q) - Quit.\n +" +
" Current Song playing: " + currentSong + "\n");
}
}
The documentation explicitly says that
Note that alternating calls to next and previous will return the same element repeatedly.
Your pictures which you have drawn at the beginning (with the pointer) are correct, you have just probably interpreted them wrong. The next()
method returns the element on the right of the pointer, and the previous()
method returns the element which is on the left of the pointer.
Maybe the picture would look more understandable with one extra comma:
// list = [<Pointer>, E1, E2, E3] -- next=E1, previous=exception
assertEquals(E1, next());
// list = [E1, <Pointer>, E2, E3] -- next=E2, previous=E1
assertEquals(E2, next());
// list = [E1, E2, <Pointer>, E3] -- next=E3, previous=E2
assertEquals(E3, next());
// list = [E1, E2, E3, <Pointer>] -- next=exception, previous=E3
assertEquals(E3, previous());
// list = [E1, E2, <Pointer>, E3] -- next=E3, previous=E2
assertEquals(E2, previous());
// list = [E1, <Pointer>, E2, E3] -- next=E2, previous=E1
assertEquals(E1, previous());
// list = [<Pointer>, E1, E2, E3] -- next=E1, previous=exception