Reputation: 577
I'm currently working on a method which creates a playlist from a music library. The idea is that it asks for a minimum rating for songs and a minimum runtime for the playlist and then produces a random list of songs which fit those criteria. If there are not enough songs of the minimum rating, it reduces the rating by 1 and then looks for songs which meet that requirement. (So if you specified that the playlist should have 60 mins of 5 star songs, but you don't have enough, it tries to fill the rest of the time with 4 star songs instead.)
However, if my library contains no 5 star songs, an infinite loop is caused in my code. (Or I assume so, anyway, the test program doesn't stop running) Why could this be?
public Playlist createPlaylist(int minRating, int minDuration) {
minDuration = minDuration * 60;
ArrayList<Track> shuffledList = trackList;
Playlist playlist = new Playlist();
Collections.shuffle(shuffledList);
int playlistDuration = 0;
while (playlistDuration <= minDuration) {
for (Track track : shuffledList) {
if (track.getRating() >= minRating && !playlist.contains(track))
{
playlist.add(track);
playlistDuration += track.getLength();
}
if (playlistDuration >= minDuration) {
break;
}
}
minRating--;
}
playlist.randomise();
return playlist;
Upvotes: 2
Views: 165
Reputation: 4080
There are several problems
(1) If the total duration of the songs is too small, it will never exit. You need an exit condition based on min rating. Since the iterator over the shuffled list is always finite, this will guarantee that the code exits.
(2) The equalities don't seem to match up. If its min duration == playlist duration, it continues iterating even though it doesnt add any tracks, which is wasteful. Change the while loop to strictly less than.
(3) It would be much more efficient if you sorted the list by rating using a treeSet with a custom comparator. then you would only have to iterate over the list of tracks once.
Upvotes: 0
Reputation: 727047
Since playlistDuration
is updated conditionally, there could be many reasons for the loop to become infinite. For example, if shuffledList
is empty, your playlistDuration
would remain zero, so the outer loop would be infinite.
You can avoid this if you adjust minDuration
to the total duration of shuffledList
before entering the loop:
minDuration = Math.Min(minDuration, shuffledList.Sum(t => t.getLength()));
In plain English this means "I cannot ask for a minimum duration of more than the total duration of shuffledList
".
You should also change the condition of your while
loop to strict "less than", because otherwise the infinite loop would remain when shuffledList
is empty.
while (playlistDuration < minDuration) {
...
}
Upvotes: 4
Reputation: 16833
Change your loop like this, exit the loop when all tracks have been browsed and rating == -1
while (playlistDuration <= minDuration) {
for (Track track : shuffledList) {
if (track.getRating() >= minRating && !playlist.contains(track))
{
playlist.add(track);
playlistDuration += track.getLength();
}
if (playlistDuration > minDuration) {
break;
}
}
minRating--;
if (minRating == -1) break;
}
Upvotes: 1
Reputation: 1578
what happens when there are not enough tracks to get playlistDuration > minDuration?
My guess is:
while( playListDuration <= minDuration )
trudges on.
Thus, you might want to check for that in your while, one option would be to add condition that tracks exist
PS: wow. While typing a couple of lines, 3 answers posted, downvote, upvote. Sheesh.
Upvotes: 0
Reputation: 4658
If you aren't finding any 5-star songs, you don't update playlistDuration
, so your while loop goes on forever.
Upvotes: 0
Reputation: 49920
Because it is possible to go through the body of the while
loop without changing playlistDuration
; in particular, when you don't add any tracks.
Upvotes: 1