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?
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.
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.
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;
}
Related
I'm creating a simple app to practice working with databases. The app has Playlists and Songs. Each Playlist contains many songs, and each Song can be in many Playlists. So it will need a many-to-many relation.
I'm trying to stick to Android's Activity->ViewModel->Repository->Database architecture using LiveData and Room.
The app's MainActivity gathers two song names and a playlist name from the user, then adds them to the Room database when the button is clicked.
Here's the Playlist object, the Song object, plus an extra object to use as a cross reference, CrossRef:
Playlist.class:
#Entity(tableName="playlist_table")
public class Playlist {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name="playlist_id")
private int playlistId;
private String name = "Default Playlist";
}
Song.class:
#Entity(tableName="song_table")
public class Song {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name="song_id")
private int songId;
private String name;
}
CrossRef.class:
#Entity(tableName="cross_ref_table", primaryKeys = {"playlist_id", "song_id"})
public class CrossRef {
#ColumnInfo(index = true, name = "playlist_id")
public int playlistId;
#ColumnInfo(index = true, name = "song_id")
public int songId;
public CrossRef(int playlistId, int songId) {
this.playlistId = playlistId;
this.songId = songId;
}
}
MainActivity gets the data from the user, calls MyViewModel to insert the data
MainActivity.class:
myViewModel.insert(playlist, songs);
then MyViewModel uses its Repository to:
Insert the playlist to the playlist_table and save it's autogenerated playlistId.
Insert each song into the song_table saving each songId.
Insert a new row in the cross_ref_table.
MyViewModel.class:
public void insert(Playlist playlist, List<Song> newSongs) {
int playlistId = (int)localRepository.insert(playlist);
int songId = 0;
for (Song song: newSongs) {
if(!songExists(song)) {
songId = (int)localRepository.insert(song);
} else {
songId = song.getSongId();
}
CrossRef crossRef = new CrossRef(playlistId, songId);
localRepository.insert(crossRef);
}
}
The Repository then calls the Dao to do the actual work.
LocalRepository.class:
public long insert(Playlist playlist){
new InsertPlaylistAsyncTask(myDao).execute(playlist);
return resultId; // Once the async task is done, return the new playlistId.
}
public long insert(Song song){
new InsertSongAsyncTask(myDao).execute(song);
return resultId; // Once the async task is done, return the new songId.
}
public void insert(CrossRef crossRef){
new InsertCrossRefAsyncTask(myDao).execute(crossRef);
}
MyDao:
#Insert
long insert(Playlist playlist); // returns a long value of the newly inserted playlistId.
#Insert
long insert(Song song); // returns a long value of the newly inserted songId.
#Insert
void insert(CrossRef crossRef);
The issue I am running into is getting autogenerated id's. They always come back as 0! In MyDao, this line should assign playlistId to the newly inserted playlist ID, right?
int playlistId = (int)localRepository.insert(playlist);
But no, it's always zero. Here's the InsertPlaylistAsyncTask in the Repository where the new id SHOULD be passed back onPostExecute:
private class InsertPlaylistAsyncTask extends AsyncTask<Playlist, Void, Long> {
private MyDao myDao;
private InsertPlaylistAsyncTask(MyDao myDao){
this.myDao = myDao;
}
#Override
protected Long doInBackground(Playlist... playlists) {
long id = 0; // TODO: make this an array and return an ArrayList<Long>
for (Playlist r:playlists) {
id = myDao.insert(r);
}
return id;
}
#Override
protected void onPostExecute(Long playlistId) {
resultId = playlistId;
}
}
If anyone has a good resource to learn more about INSERTing to a database with many-to-many relations, I'm all ears! Thanks all.
I think I found my own solution. Use a handler!
Create an object PlaylistWithSongs.
Call the repository to insertPlaylistWithSongs.
Now insert the Playlists and Songs in their tables:
The repository inserts the Playlist in a second AsyncTask which calls a handler on the main thread with the playlistId in hand.
The repository inserts each song in the list in more AsyncTasks, calling the same handler with each new songId generated.
The handler is on the main thread, waiting for ids to roll in. Every time it sees a valid playlistId and a valid songId, it inserts the match into the crossRef table.
PlaylistWithSongs.java:
public class PlaylistWithSongs {
#Embedded
public Playlist playlist;
// The songs variable will contain all songs related by the playlistId and songId.
#Relation(
parentColumn = "playlist_id",
entityColumn = "song_id",
associateBy = #Junction(CrossRef.class)
)
public List<Song> songs;
}
LocalRepository.java:
public void insertPlaylistWithSongs(PlaylistWithSongs playlistWithSongs) {
MyDao myDao;
insertPlaylist(playlistWithSongs.getPlaylist());
for (Song song : playlistWithSongs.getSongs()) {
insertSong(song);
}
tempPlaylistId = 0;
tempSongId = 0;
}
insertPlaylist inserts the playlist in the table and saves the new id id = (int)myDao.insert(playlist);. It packages a message containing the new id ids.putInt(PLAYLISTID, id); to send to the handler on the main thread handler.sendMessage(msg);:
public void insertPlaylist(Playlist playlist){
new InsertPlaylistAsyncTask(myDao).execute(playlist);
}
private class InsertPlaylistAsyncTask extends AsyncTask<Playlist, Void, Void> {
private MyDao myDao;
private InsertPlaylistAsyncTask(MyDao myDao){
this.myDao = myDao;
}
#Override
protected Void doInBackground(Playlist... playlists) {
int id = 0;
for (Playlist playlist:playlists) {
Playlist retrievedFromDatabase = myDao.getPlaylistByName(playlist.getName());
if(retrievedFromDatabase == null){
// If playlist name doesn't exist
id = (int)myDao.insert(playlist);
} else {
// Else set the id to the existing playlist id
id = retrievedFromDatabase.getPlaylistId();
}
// Pass the new id to the main thread once the background insert is complete.
Bundle ids = new Bundle();
ids.putInt(PLAYLISTID, id);
Message msg = new Message();
msg.setData(ids);
msg.setTarget(handler);
handler.sendMessage(msg);
}
return null;
}
}
Each song is handled the exact same way, so I won't repeat the code.
Each time the handler receives a new id, it updates the Repository's static variables, private static int tempSongId = 0; and private static int tempPlaylistId = 0;, then checks if it has enough valid information to insert the crossRef:
Handler handler = new Handler(){
#Override
public void handleMessage(#NonNull Message msg) {
Bundle ids = msg.getData();
if(ids.getInt(PLAYLISTID) != 0){
tempPlaylistId = msg.getData().getInt(PLAYLISTID);
}
if(ids.getInt(SONGID) != 0){
tempSongId = msg.getData().getInt(SONGID);
}
if(tempPlaylistId!=0 && tempSongId!=0) {
// if both ids were retrieved
CrossRef crossRef = new CrossRef(tempPlaylistId, tempSongId);
Log.d(TAG, "handler insert(crossRef): " + tempPlaylistId + "," + tempSongId);
insertCrossRef(crossRef);
}else{
// if not, do nothing. We need both ids to insert a crossRef entry.
}
}
};
One issue with this code: If the main activity stops before the AsyncTasks are complete, a memory leak could occur. Perhaps adding a weak reference could fix that problem.
I have created a class called Album, which is this one:
public class Album {
private String Titulo;
private int temas;
private int ano;
public Album(String Titulo2, int temas2, int ano2) {
this.Titulo = Titulo2;
this.temas = temas2;
this.ano = ano2;
}
public Album(String Titulo2, int temas2) {
this.Titulo = Titulo2;
this.temas = temas2;
}
public int getAno() {
return this.ano;
}
public int getTemas() {
return this.temas;
}
public String getTitulo() {
return this.Titulo;
}
public void setAno(int ano) {
this.ano = ano;
}
public boolean foiEditadoNesteSeculo() {
if (this.ano > 2000) {
return true;
} else {
return false;
}
}
public void adicionaTemasBonus(int x) {
this.temas += x;
}
public void mostraAlbum() {
System.out.println(this.Titulo + " (editado em " + this.ano + "; tem " + this.temas + " temas)");
}
}
It works fine. The problem is that the teacher asked me to create a new class called Band and it has to have an array of Albums. The Band object should be declared with an int that represents the limit of the number of albums (the length of the array). I already have some idea on how to work with arrays, but I have no idea on how to create a type of array that contains objects from another class, and after how to use the attributes of the objects to return something. I think I can figure out the rest after I'm able to properly create the class, though.
Apologies, as it has been described in Portuguese and I don't have much experience in translating.
In my opinion this would be easier to manage with a List so you can add as many Albums as you want at any time, however, since the problem statement required Array I made an example of a Band class.
I also included main method to test the program at the bottom of the Band class:
public class Band {
private int totalAlbums;
private Album[] albums;
private int currentNumberOfAlbums;
public Band(int totalAlbums) {
this.totalAlbums = totalAlbums;
this.albums = new Album[totalAlbums];
this.currentNumberOfAlbums = 0;
}
public Band(Album[] albums) {
this.totalAlbums = albums.length;
this.albums = albums;
this.currentNumberOfAlbums = this.totalAlbums;
}
public void addNewAlbum(String titulo, int temas, int ano) {
if (this.currentNumberOfAlbums == totalAlbums) {
System.out.println("Warning: Cannot add any more albums, limit reached.");
return;
}
this.albums[this.currentNumberOfAlbums++] = new Album(titulo, temas, ano);
}
public void printAlbums() {
for (Album a : this.albums) {
a.mostraAlbum();
}
}
public static void main(String [] args) {
Band b = new Band(3);
b.addNewAlbum("The First", 4, 2001);
b.addNewAlbum("The Second", 98, 2055);
b.addNewAlbum("The Finale", 12, 2011);
b.addNewAlbum("The Extra", 12, 2111);
b.printAlbums();
}
}
There are a few things to look for in this code.
First, to address your direct question, you can simply use a custom class as an array like any other class/primitive with Album[].
Secondly, you will require a Band constructor that instantiates the array of Album based on an integer passed to it, so you know how many albums are the limit. You can see this with the this.albums = new Album[totalAlbums]; line.
Next, you need a way to add a new Album into the array of Album[]. This can be done a few different ways, but the way I chose was to create a method addNewAlbum(String, int, int) to do it for this example which will also increase currentNumberOfAlbums by 1 every time a new album is added. This is useful so you know when an Album is attempted to be added even though the totalAlbums are already full! This will prevent an ArrayIndexOutOfBoundsException in your code if addNewAlbum is called too many time.
Lastly, in addNewAlbum you need to call your Album constructor with new Album(titulo, temas, ano).
In my example main, a Band with limit of 3 albums is created, and 4 albums are attempted to be added into it, with the first 3 adding successfully, and the 4th not being added, but instead printing a warning for being outside the limit.
I also added a printAlbums() method which will use your mostraAlbum() to print each Album in the albums array.
Output:
Warning: Cannot add any more albums, limit reached.
The First (editado em 2001; tem 4 temas)
The Second (editado em 2055; tem 98 temas)
The Finale (editado em 2011; tem 12 temas)
EDIT:
I added the Band(Album[] albums) constructor, you can call this with:
Album[] albums = new Album[3];
//Add your albums into this variable
Band b = new Band(albums);
public class Band {
private Album[] albums;
private numberOfAlbums;
//...
// create an empty constructor
Band(){
albums = new Album[];
numberOfAlbums = 0;
}
// constructor that receives the albums
Band(Album[] albums){
this.albums = albums;
this.numberOfAlbums = albums.length;
}
// constructor that receives the number of albums
Band(int numOfAlbums){
this.numberOfAlbums = numOfAlbums;
this.albums = new Album[numOfAlbums];
}
// add getters and setters
// example of adding a new album
public void addNewAlbum(Album album){
if(this.numOfAlbums == this.albums.length){
// you need to create a new array with a bigger size, copy the existing data and insert the album
// or whatever you'd like
} else {
this.albums[this.numOfAlbums] = album;
// increment the numOfAlbums
this.numOfAlbums++;
}
}
}
private class Album {
//...
}
You just need to add [] to define that the field is an array.
public class Band {
private int totalAlbums;
private Album[] albums;
//...
}
private class Album {
//...
}
I hope this example helps you.
private Album[] albums; // array of album
private int albumLimit; // limit for album
public Band(int albumLimit) {
this.albumLimit = albumLimit; // initialize limit
this.albums = new Album[albumLimit]; // set limit of album array
}
// here it creates a new Album every time the loop runs
// you can fill the array in other ways too
public void fillAlbum() {
for (int i = 0; i < albumLimit; i++) {
String name = "name_" + i;
int team = i;
albums[i] = new Album(name, team);
}
}
public void printAlbum() {
for (int i = 0; i < albumLimit; i++) {
System.out.println("Name :" + albums[i].getTitulo());
System.out.println("Team :" + albums[i].getTemas());
System.out.println();
}
}
}
I have a Song class(field: String name) and an Album class (initialize an Arraylist fill with Song objects), I am adding a method (addSongAlbum(String name parameter))that first check if the song is already in the album. Everything works fine but now I want to check too if the song exist outside the album. How can I do this taking in consideration that the input of the method is a String?
public void addSongToAlbum(String name){
if(checkExist(name) == null){
album.add(checkExist(name));
System.out.println("Song "+name+" was successfully added");
} else {
System.out.println("This song is already in the album");
}
}
private Song checkExist(String name){
for(int i=0; i<album.size(); i++){
if(name.equals(album.get(i).getName())){
return album.get(i);
}
}
return null;
}
I'd create a central Songmanager object that is available to all objects working with Songs, like so
public class SongManager {
private static SongManager sMan;
private Map<String, Song> songs = new HashMap<String, Song>();
public static SongManager getInstance() {
if (sMan == null) {
sMan = new SongManager();
}
return sMan;
}
public void addSong(Song s) {
songs.put(s.getName, s);
}
public Song getSong(String name) {
return songs.get(name);
}
}
your songs can be simple container classes, like so
public class Song {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
You can access this manager everywhere by calling SongManager.getInstance(). Create a simple container class for songs all existing Songs to this Manager and in youraddSongMethod, simply call thegetSong` method to see if the song exists.
P.s. I know that the Singleton pattern I'm using here is very controversal (see e.g. What is so bad about singletons?, but to me it is the easiest working alternative here.
You would need to have a List of all the Song objects somewhere. Either add them to a static List when constructing (you would need to remember to remove them when no longer used to let the memory be garbage collected), or have a similar List of all Album objects and then search through those. The first one is useful if you want to check all Song objects that were created, the second one if you only want to check those that are in albums.
I have an ArrayList that contains a number of songs objects.
ArrayList<Song> aSong = new ArrayList<Song>();
The Song class looks like this:
public class Song {
private String sName;
private String sLength;
public Song(String name, String length){
sName = name;
sLength= length;
}
public String getName(){
return sName;
}
public String getLength(){
return sLength;
}
public void edit(String name, String length){
sName = name;
sLength= length;
}
}
I'm trying to see if a song exists with the ArrayList method "contains"
// example
aSong.contains("Song 1")
but this returns false even though the song does really exist in the ArrayList.
Any help would be highly appreciated and thank you in advance.
No, "Song 1" definitely doesn't exist in the ArrayList - because that's a string, not a Song.
Three options:
Check for each song in the list to see whether it has that name. This is reasonably easy to do, but if you need that functionality in multiple places you'd definitely want to extract it into a separate method. Something like:
public static Song findSongByName(Iterable<? extends Song> songList,
String name)
{
// TODO: Nullity checks
for (Song song : songList)
{
if (song.getName().equals(name))
{
return song;
}
}
return null;
}
Override equals in Song based on the name of the song, and then call aSong.contains(new Song("Song 1"). This probably isn't a good idea, as I suspect that if you've got two Song objects with the same name but different lengths, you don't want to consider them as equal.
Build a Map<String, Song> so that you can look up songs in the list based on their name. This would be a good idea if you needed to look up multiple songs from the same list.
"Song 1" is a string, it's not the same as any Song object. Sounds to me that you are still at the stage where you think the language can guess what you want, rather than realising you have to tell it exactly and precisely what you want.
You could write a for loop and go though your list one by one, looking for the right title. Or perhaps you should switch to using a HashMap<String, Song>, that would let you look up a song by it's title.
You can define a function find that searches for and returns the first object that satisfies the given predicate.
public static <A> A find(final Iterable<A> iterable, final Predicate<A> predicate) {
for(final A item : iterable) {
if(predicate.isSatisfiedBy(item)) {
return item;
}
}
return null;
}
where Predicate is:
interface Predicate<A> {
boolean isSatisfiedBy(final A a);
}
You could use this function for your purpose as shown below:
final Song song = find(songs,
new Predicate<Song>() {
boolean isSatisfiedBy(final Song s) {
return s.getName().equals("Song 1");
}
}
);
I am trying to make a project that adds cd / dvd /movie info from main() to a collections library
then prints info added.
like: output
-Book-
author: Robert A. Heinlein
# pages: 325
title: Starship Troopers
keywords: science fiction, war, weapons
-Music-
band: five finger death punch
# songs: 15
members: Zoltan Bathory, Ivan Moody,Jeremy Spencer,Matt Snell,Jason Hook
title: War is the answer
keywords: rock
I currently have 6 classes
1.project1 - main()
2.Library - where im adding to database
3.item - inheritance(title & number)
4.cd
5.dvd
6.movie
i am trying to use inheritance so i want to keep the files i have.
My question is i am trying to add to the collections in the library class. I am just not sure how to do it.
here is the classes i think you will need to see..
import java.io.PrintStream;
import java.util.Collection;
public class project
{
private static Library library = new Library();
public static void main(String[] args)
{
PrintStream out = System.out; // we will be printing to the standard output stream
Item item;
// add items to library
out.println(">>> adding items to library:\n");
item = library.addBook("The Curious Incident of the Dog in the Night-Time", "Mark Haddon", 240, "autism", "Asperger's Syndrome");
if (item != null)
library.printItem(out, item);
item = library.addBook("Starship Troopers", "Robert A. Heinlein", 325, "science fiction", "war", "weapons");
if (item != null)
library.printItem(out, item);
item = library.addBook("The Moon Is A Harsh Mistress", "Robert A. Heinlein", 389, "science fiction", "moon", "social structures");
if (item != null)
library.printItem(out, item);
item = library.addMusicCD("Europe In '72", "Grateful Dead", 12, "acid rock", "sixties", "jam bands");
if (item != null) {
library.addBandMembers(item, "Jerry Garcia", "Bill Kreutzman", "Keith Godcheaux");
library.printItem(out, item);
}
item = library.addMusicCD("Don't Let Go", "Jerry Garcia Band", 15, "acid rock", "jam bands");
if (item != null) {
library.addBandMembers(item, "Jerry Garcia", "Keith Godcheaux");
library.printItem(out, item);
}
item = library.addMusicCD("Sergeant Pepper's Lonely Hearts Club Band", "Beatles", 10, "acid rock", "sixties");
if (item != null) {
library.addBandMembers(item, "John Lennon", "George Harrison", "Ringo Starr");
library.printItem(out, item);
}
item = library.addMovieDVD("Lost In Translation", "Sofia Coppola", 14, "Japan", "loneliness");
if (item != null) {
library.addCast(item, "Bill Murray", "Scarlett Johansson");
library.printItem(out, item);
}
item = library.addMovieDVD("Groundhog Day", "Harold Ramis", 14, "newscaster", "groundhog", "time");
if (item != null) {
library.addCast(item, "Bill Murray", "Andie MacDowell");
library.printItem(out, item);
}
// print books, musicCDs, movies
out.println(">>> books:\n");
printItems(out, library.books());
out.println(">>> music CDs:\n");
printItems(out, library.musicCDs());
out.println(">>> movies:\n");
printItems(out, library.movies());
// print items for keyword
printItemsForKeyword(out, "science fiction");
printItemsForKeyword(out, "jam bands");
printItemsForKeyword(out, "xxx");
// items by artist
out.println(">>> books by Robert A. Heinlein:\n");
printItems(out, library.booksByAuthor("Robert A. Heinlein"));
out.println(">>> music by the Grateful Dead:\n");
printItems(out, library.musicByBand("Grateful Dead"));
out.println(">>> music by the Rolling Stones:\n");
printItems(out, library.musicByBand("Rolling Stones"));
out.println(">>> movies by Sofia Coppola:\n");
printItems(out, library.moviesByDirector("Sofia Coppola"));
out.println(">>> music by Jerry Garcia:\n");
printItems(out, library.musicByMusician("Jerry Garcia"));
out.println(">>> movies with Bill Murray:\n");
printItems(out, library.moviesByActor("Bill Murray"));
}
private static void printItemsForKeyword (PrintStream out, String keyword)
{
Collection<Item> items;
out.printf(">>> items for keyword: %s\n\n", keyword);
items = library.itemsForKeyword(keyword);
printItems(out, items);
}
private static void printItems (PrintStream out, Collection<Item> items)
{
if (items != null && items.size() > 0)
for (Item item : items)
library.printItem(out, item);
else
out.println("none\n");
}
}
here is the library class where i am having trouble adding to the collections..
How would i add a book or a cd to the collections?
import java.io.PrintStream;
import java.util.Collection;
import java.util.*;
public class Library
{
// returns all of the items which have the specified keyword
public Collection<Item> itemsForKeyword(String keyword)
{
return null;
}
// print an item from this library to the output stream provided
public void printItem(PrintStream out, Item item)
{
}
// adds a book to the library
public Item addBook(String title, String author, int nPages, String... keywords)
{
return null;
}
// returns all of the books by the specified author
public Collection<Item> booksByAuthor(String author)
{
return null;
}
// returns all of the books in the library
public Collection<Item> books()
{
return null;
}
// music-related methods
// adds a music CD to the library
public Item addMusicCD(String title, String band, int nSongs, String... keywords)
{
Collection MusicCollection = new HashSet();
MusicCollection.add(title);
return null;
}
// adds the specified band members to a music CD
public void addBandMembers(Item musicCD, String... members)
{
}
// returns all of the music CDs by the specified band
public Collection<Item> musicByBand(String band)
{
return null;
}
// returns all of the music CDs by the specified musician
public Collection<Item> musicByMusician(String musician)
{
return null;
}
// returns all of the music CDs in the library
public Collection<Item> musicCDs()
{
return null;
}
// movie-related methods
// adds a movie to the library
public Item addMovieDVD(String title, String director, int nScenes, String... keywords)
{
return null;
}
// adds the specified actors to a movie
public void addCast(Item movie, String... members)
{
}
// returns all of the movies by the specified director
public Collection<Item> moviesByDirector(String director)
{
return null;
}
// returns all of the movies by the specified actor
public Collection<Item> moviesByActor(String actor)
{
return null;
}
// returns all of the movies in the library
public Collection<Item> movies()
{
return null;
}
}
here is the items class
import java.io.PrintStream;
import java.util.Collection;
import java.util.*;
public class Item
{
private String title;
private int number;
public Item(String theTitle, int theNumber)
{
number = theNumber;
title = theTitle;
}
public String getTitle()
{
return title;
}
public int getNumber()
{
return number;
}
}
here is the cd class - the dvd class is almost identical
import java.util.*;
public class CD extends Item
{
private String artist;
private String members;
public CD(String theTitle, String theArtist, String theMembers, int number)
{
super(theTitle,number);
artist = theArtist;
members = theMembers;
}
public String getArtist()
{
return artist;
}
public String getMembers()
{
return members;
}
public void print()
{
System.out.println("-Music-");
System.out.println("band: " + artist);
}
}
I am not sure if i could combine the cd/dvd/movie classes into items class?
My main QUESTION is:
how should i add each cd/dvd to collections?????
in the library class
would i just define a collection to add in every addfunction(addMusicCD,addBandMembers,addMovieDVD,etc..) or should i put the collection in the beginning of the class? and how do i add to that collection???
public Item addMusicCD(String title, String band, int nSongs, String... keywords)
{
Collection MusicCollection = new HashSet(); // how should i add each cd/dvd to collections?????
MusicCollection.add(title);
return null;
}
I am also trying to return an Item and cannot! what item would i need to return??
I know this is alot of information. Sorry.
Hopefully someone can help me and not just make fun of me. i am trying to learn java from c++
Thank you for any help you can give me..
I think you need to study Object Orientation Principles a bit more.
The Library should be able to add Items. The Library methods addBook(many params) and addDVD() etc should be replaced by a more generic addItem(Item item, String... keywords).
Items can be CDs, DVDs or Movies. It's up the the CD class to add band members, not the Library class.
Adding an item to the library becomes something like
CD cd = new CD("Europe In '72", "Grateful Dead", 12);
cd.addBandMembers("Jerry Garcia", "Bill Kreutzman", "Keith Godcheaux");
library.addItem(cd, "acid rock", "sixties", "jam bands"));
Hope this helps a little to get you on track.
In addMusicCD, do a new CD(the various bits).
Add the resulting CD to the collection.
Return it.
It might be easier for you to fit this all together if you used Generics to declare collections, e.g.
class Library {
private Set<CD> theCDs;
public Item addCD(String title) {
CD cd = new CD(title);
theCDS.add(cd);
return cd;
}
}
etc, etc. etc.
I would probably add another tree of inheritance, so a Library is an abstract subclass of e.g. HashSet:
public abstract class Library<T extends Item> extends HashSet<T>{
...
abstract void addItem(T item);
}
Then you create all your libraries by subclassing your Library-class:
public class CDLibrary extends Library<Cd>{
...
#Override
public void addItem(Cd item){
// Maybe add some information of cd to a hashmap for lookups, or whatever
...
this.add(item);
}
When you're subclassing HashSet, all the add and delete operations are done for you.
If you dont need specific add-methods for each Item, you can simply remove the abstract mathod of Library, and just take advantage of the generic-syntax, to specify a new type of library when subclassing.
You might consider subclassing ArrayList, or something similar instead, as HashSet does not have a get() method, which ArrayList does, the above code was just an example of what you could do.
If its unclear, I'll try to clarify a bit more! But I hope you get the picture - subclassing to inherit the functions you need, instead of creating them again. Generics, (do you know generics?) are to ensure type-safety, so you cannot add a DVD to a CDLibrary.
Also, be sure to override equals() and hashcode() of your Items, to make sure you can distinguish between two Items.
I hope it makes sense!
Do you need to have the CDs, DVDs, and Books into separate data structures? What's your use case? If you need to get them in a separate way, then having different data structures is OK.
Otherwise, and I think this is the case, I think you could be fine having a Set<Item> and dump all your items in it.
public static class Library{
private Set<Item> items = new HashSet<Item>();
public void add(Item i){
items.add(i);
}
public String toString(){
return items.toString()
}
}
And, in your Item sub-classes, you have to have toString() overridden, and everything will print itself just fine.