Java Java Data Structures Efficiency! Custom Serialization

Zeljko Porobija
Zeljko Porobija
11,490 Points

The messed-up artists list

Well, everything was fine until the very last changes. And the list of artists now goes like this: Available artists:
1.) J
2.) K
3.) M
4.) R
5.) T
6.) U
What did I do wrong?

Zeljko Porobija
Zeljko Porobija
11,490 Points
SongBook.java
package com.teamtreehouse.model; /

import java.io.*;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class SongBook {
 private List<Song> mSongs;

 public SongBook() {
  mSongs = new ArrayList<Song>(); 
 }

 public void exportTo(String fileName) {
  try (
   FileOutputStream fos = new FileOutputStream(fileName);
    PrintWriter writer = new PrintWriter(fos);
  ) {
    for (Song song : mSongs) {
     writer.printf("%s|%s|%s|n",
                   song.getArtist(),
                   song.getTitle(),
                   song.getVideoUrl());

    }
  } catch (IOException ioe) {
   System.out.printf("Problem saving %s %n", fileName);
   ioe.printStackTrace();    
  }
 }

 public void importFrom(String fileName) {
   try (
     FileInputStream fis = new FileInputStream(fileName);
     BufferedReader reader = new BufferedReader (new InputStreamReader(fis));
   ) {
     String line;
     while (( line = reader.readLine()) != null) {
      String [] args = line.split("\\ |");
      addSong(new Song(args[0], args[1], args[2]));
     }
   } catch(IOException ioe) {
     System.out.printf("Problems loading %s /n", fileName);
     ioe.printStackTrace();
   }
 }

  public void addSong(Song song) {
  mSongs.add(song); 
 }

 public int getSongCount() {
  return mSongs.size(); 
 }

 //FIXME: This should be cached!
  private Map<String, List<Song>> byArtist(){
  Map<String, List<Song>> byArtist = new TreeMap<>();
 for (Song song : mSongs) {
  List<Song> artistSongs = byArtist.get(song.getArtist());
   if (artistSongs == null) {
     artistSongs = new ArrayList<>();
     byArtist.put(song.getArtist(), artistSongs);
   }
   artistSongs.add(song);
 }
   return byArtist;
 }

  public Set<String> getArtists() {
   return byArtist().keySet(); 
  }

  public List<Song> getSongsForArtist(String artistName) {
    List<Song> songs = byArtist().get(artistName); 
   //Comparator interface + anonymous inline class
     songs.sort(new Comparator<Song>() {
     @Override
    public int compare(Song song1, Song song2) {
       if (song1.equals(song2)) {
       return 0; 
        }
         return song1.mTitle.compareTo(song2.mTitle);
      }
    });
     return songs;
  }

}```
Zeljko Porobija
Zeljko Porobija
11,490 Points
Song.java
package com.teamtreehouse.model;

public class Song {
 protected String mArtist;
 protected String mTitle;
 protected String mVideoUrl;

 public Song (String artist, String title, String videoUrl) {
   mArtist = artist;
   mTitle = title;
   mVideoUrl = videoUrl;   
 }
  public String getTitle() {
   return mTitle; 
  }
  public String getArtist() {
   return mArtist; 
  }
  public String getVideoUrl() {
   return mVideoUrl; 
  }

  @Override
  public String toString() {
    return String.format("Song: %s by %s", mTitle, mArtist);
  }
}```

6 Answers

Steve Hunter
MOD
Steve Hunter
Treehouse Moderator 57,627 Points

Hi there,

That output is created by the promptArtist method in the KaraokeMachine class. It should look like:

  private String promptArtist() throws IOException {
    System.out.println("Available artists: ");
    List<String> artists = new ArrayList<>(mSongBook.getArtists());
    int index = promptForIndex(artists);
    return artists.get(index);
  }

It calls getArtists() which I can see is correct in your code. Also, it calls promptForIndex which should look like:

  private int promptForIndex(List<String> options) throws IOException {
    int counter = 1;
    for(String option : options){
      System.out.printf("%d.) %s %n", counter, option);
      counter++;
    }
    System.out.print("Your choice:  ");
    String optionAsString = mReader.readLine();
    int choice = Integer.parseInt(optionAsString.trim());
    return choice - 1;
  }

That outputs the string that seems to be amiss in your code.

Is your's the same?

Steve.

Zeljko Porobija
Zeljko Porobija
11,490 Points
KarokeMachine.java
package com.teamtreehouse;

import com.teamtreehouse.model.Song;
import com.teamtreehouse.model.SongBook;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;


public class KaraokeMachine {
  private SongBook mSongBook;
  private BufferedReader mReader;
  private Queue<Song> mSongQueue;

  private Map<String, String> mMenu;

  public KaraokeMachine(SongBook songBook) {
    mSongBook = songBook;
    mReader = new BufferedReader(new InputStreamReader(System.in));
    mSongQueue = new ArrayDeque<Song>();
    mMenu = new HashMap<String, String>();
    mMenu.put("add", "Add a new song to the song book");
    mMenu.put("play", "Play next song in the queue");
    mMenu.put("choose", "Choose a song to sing!");
    mMenu.put("quit", "Give up. Exit the program");
    }

  private String promptAction() throws IOException {
   System.out.printf("There are %d songs available and %d in the queue. Your options are: %n", 
                     mSongBook.getSongCount(),
                     mSongQueue.size());
    for (Map.Entry<String, String> option: mMenu.entrySet()) {
      System.out.printf("%s - %s %n",
                        option.getKey(),
                        option.getValue());
  }
    System.out.print("What do you want to do: ");
    String choice = mReader.readLine();
    return choice.trim().toLowerCase();
  }

  public void run() {
      String choice = "";
      do {
        try {
          choice = promptAction();
          switch(choice) {
            case "add":
            Song song = promptNewSong();
            mSongBook.addSong(song);
            System.out.printf("%s added! %n%n", song);
            break;
            case "choose":
              String artist = promptArtist();
              Song artistSong = promptSongForArtist(artist);
              mSongQueue.add(artistSong);
              System.out.printf("You chose: %s %n", artistSong);
            break; 
            case "play":
              playNext();
              break;
            case "quit":
              System.out.println("Thanks for playing!");
            break;
            default:
              System.out.printf("Unknown choice: '%s'. Try again. %n%n%n", 
              choice);
          }
        } catch (IOException ioe) {
         System.out.println("Problem with input");
         ioe.printStackTrace();
        }
      } while(!choice.equals("quit"));
   }

private Song promptNewSong() throws IOException {
 System.out.print("Enter the artist's name: ");
 String artist = mReader.readLine();
 System.out.print("Enter the title: ");
 String title = mReader.readLine();
 System.out.print("Enter the video URL: ");
 String videoUrl = mReader.readLine();
 return new Song(artist, title, videoUrl);
}

  private String promptArtist() throws IOException {
    System.out.println("Available artists:");
    List<String> artists = new ArrayList<>(mSongBook.getArtists());   
    int index = promptForIndex(artists);
    return artists.get(index);
  }

  private Song promptSongForArtist(String artist) throws IOException {
    List<Song> songs = mSongBook.getSongsForArtist(artist);
    List<String> songTitles = new ArrayList<>();
    for (Song song : songs) {
      songTitles.add(song.getTitle());
    }
  System.out.printf("Available songs for %s: %n", artist);
  int index = promptForIndex(songTitles);
  return songs.get(index);
  }

  private int promptForIndex(List<String> options) throws IOException {
   int counter = 1;
   for (String option : options) {
     System.out.printf("%d.) %s %n", counter, option);
     counter++;
  }
System.out.print("Your choice: ");
String optionAsString = mReader.readLine();
int choice = Integer.parseInt(optionAsString.trim());
return choice - 1;//We started with counter = 1 while array is zero based
  }   

public void playNext() {
  Song song = mSongQueue.poll();
  if (song == null) {
   System.out.println("Sorry there are no songs in the queue"
                    + "Use choose from the menu to add some"); 
  } else {
    System.out.printf("%n%n%n Open %s to hear %s by %s %n%n%n",
                      song.getVideoUrl(),
                      song.getTitle(),
                      song.getArtist());
    }
  } 
}```
Zeljko Porobija
Zeljko Porobija
11,490 Points
Karaoke.java
import com.teamtreehouse.KaraokeMachine;
import com.teamtreehouse.model.Song;
import com.teamtreehouse.model.SongBook;

public class Karaoke {

  public static void main(String[] args) {
    SongBook songBook = new SongBook();
    songBook.importFrom("songs.txt");
    KaraokeMachine machine = new KaraokeMachine(songBook);
    machine.run(); 
    System.out.println("Saving book....");
    songBook.exportTo("songs.txt");
  }
}```
Steve Hunter
MOD
Steve Hunter
Treehouse Moderator 57,627 Points

Very strange ... let's do some breakpoints.

Can you add a breakpoint inside promptArtist where we set the index;

int index = promptForIndex(artists);

We need to see what's in the ArrayList that we pass as a parameter. If you put the breakpoint there, then run the code in debug mode, execution should pause at the breakpoint and you should be able to see the contents of the artists List. What does that look like?

Steve.

Zeljko Porobija
Zeljko Porobija
11,490 Points

How do I add breakpoints within the workspace console? I have no clue....

Steve Hunter
Steve Hunter
Treehouse Moderator 57,627 Points

Sorry - I've got mine in an IDE, not Workspaces. There is no debugging facility, no.

Let me put your code into IntelliJ to see if I can replicate the error - give me a minute, or three! Do I have all your files above? Can you post me a link to your workspace, I'll pull your code from there.

Zeljko Porobija
Zeljko Porobija
11,490 Points

https://teamtreehouse.com/workspaces/14424562 Feel free to use it, to write your code, to do lists, memories from your childhood, whatever. :-) I'm just going to install IntelliJ and then to get a clear idea of how to debug etc. (I know that on e.g. JavaScript, but, as you might know, JavaScript is not Java :-) ). Thanks in advance!

Steve Hunter
Steve Hunter
Treehouse Moderator 57,627 Points

That link doesn't work.

Shout once you've got IntelliJ working; there's a course on here to help set it up. It helps!

https://teamtreehouse.com/library/local-development-environments

Zeljko Porobija
Zeljko Porobija
11,490 Points

Yep, I'm just watching that. I'll play for a while with my Linux and then with Windows, just to see more exactly how it works. No problems with that. Although I'm new to Java, I'm not a newbie in programming.