这个Java程序中为什么do-while循环不能按预期运行?

8
我正在编写一个程序,可以播放音乐文件名的数组。我已经成功实现了这个功能,但是想要增强一些功能并使它更加美观。我试图使音乐以随机顺序播放,但在整个列表播放完之前不重复任何歌曲。我几乎做到了,但我认为我的do-while循环有些问题。该程序在播放约八首歌曲后停止播放音乐,但JVM仍然在运行。由于我仍然是AP计算机科学学生,所以我使用BlueJ,意识到我可能无法完成此任务,但任何帮助将不胜感激。我有一个驱动器“MusicDriver”,与其他两个类“MP3”和“Music”存在“has-a”关系。

我的MP3类:

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import javazoom.jl.player.Player;

public class MP3 {
    String filename;
    Player player; 

    public void stopMP3() { if (player != null) player.close(); }

    // play the MP3 file to the sound card
    public void playMP3(String filename) {
    try {
        FileInputStream fis = new FileInputStream(filename);
        BufferedInputStream bis = new BufferedInputStream(fis);
        player = new Player(bis);
    }
    catch (Exception e) {
        System.out.println("Problem playing file " + filename);
        System.out.println(e);
    }

    // run in new thread to play in background
    new Thread() {
        public void run() {
            try { player.play(); }
            catch (Exception e) { System.out.println(e); }
        }
    }.start();
}  
}

我的音乐课:

import java.util.*;

public class Music{
private ArrayList<String> music;

public Music(){music = new ArrayList<String>();}

public int size(){return music.size();}

public void addSong(String song){music.add(song);}

public String getSong(){return music.get(music.size());}

public String getSong(int num){return music.get(num);}

public void removeSong(String song){
    for(int i = 0; i < music.size(); i++){
        if(music.get(i).equals(song)) {music.remove(i); return;}
    }
}

public String toString(){
    String s = "";
    for(int i = 0; i < music.size(); i++){
        s += music.get(i);
    }
    return s;
}
}

我的MusicDriver类:

import java.util.*;
import java.io.*;
import javazoom.jl.player.Player;
import java.util.Random;
import java.util.Scanner;
import java.io.FileNotFoundException;

public class MusicDriver{
public static void main(String[] args) throws FileNotFoundException{
    Random r = new Random();
    Scanner s = new Scanner(System.in);
    String line = "";
    int number;

    Music song = new Music();
    song.addSong("1-01-overture.mp3");
    song.addSong("1-03-fortune-teller-2.mp3");
    song.addSong("1-07-prayer.mp3");
    song.addSong("1-08-island-atlas.mp3");
    song.addSong("1-12-warren-report.mp3");
    song.addSong("1-13-avilla-hanya.mp3");
    song.addSong("1-20-war-situation.mp3");
    song.addSong("2-10-fog-of-phantom.mp3");
    song.addSong("2-12-religious-precepts.mp3");
    song.addSong("2-14-box-of-sentiment.mp3");
    song.addSong("3-02-light-everlasting.mp3");
    song.addSong("3-09-viking-spirits.mp3");
    song.addSong("3-12-unsealed.mp3");
    song.addSong("3-16-notice-of-death-reprise-.mp3");
    //14 songs

    ArrayList<Integer> songNums = new ArrayList<Integer>();
    MP3 mp3 = new MP3();
    do{
        if(songNums.size() == song.size()) songNums.clear();

        number = r.nextInt(song.size());
        boolean done = false;
        int counter = 0;
        while(!done){
            for(int i = 0; i < songNums.size(); i++){
                if(number == songNums.get(i).intValue()) {number = r.nextInt(song.size()); counter++;}
            }
            if(counter == 0) done = true;
            else done = false;
        }

        songNums.add(number);
        mp3.playMP3(song.getSong(number));
        System.out.println("Now Playing " + song.getSong(number));
        System.out.println("Enter \"Stop\" to stop playing the song");
        System.out.println("Enter \"n\" to play the next song");
        line = s.nextLine();
        mp3.stopMP3();
    }while(line.equals("n"));
    mp3.stopMP3();
}
}

我已经对为什么我的程序停止播放歌曲进行了大量研究,但是我还没有找到任何结果。我确实发现,BlueJ程序在输出之前请求输入时不会打开终端窗口(当您执行“System.out.print()”时出现的东西),但我不认为这与该程序有关。我还确认了当我想播放下一首歌曲时输入了一个字符串“n”,并且在前几首歌曲中它起作用了,但在第八首歌曲后它就停止了。我非常困惑。

11
只需使用 Collections.shuffle() 方法对列表进行洗牌,然后在 for-each 循环中逐一播放歌曲。 - Alex Salauyou
在do-while循环结束之前调用stopMP3是强制性的吗?我怀疑这可能是过早停止的原因。 - Sendhilkumar Alalasundaram
你有没有碰巧遇到任何异常? - Drew Kennedy
1
似乎您需要某种同步机制(以便在歌曲播放完毕后查询输入)。现在MP3#playMP3()不是阻塞的,因此它会在歌曲开始播放后立即返回(当您不需要询问“播放下一首?”时)。 - Alex Salauyou
1
@SendhilkumarAlalasundaram 我认为这里的主要问题在于缺乏对哪些方法应该是阻塞的,哪些不应该,它们如何在线程中运行以及如何正确同步所有这些东西的理解。 - Alex Salauyou
显示剩余2条评论
4个回答

7

我认为唯一的问题在于您在洗牌列表时使用的逻辑。

number = r.nextInt(song.size());
boolean done = false;
int counter = 0;
while(!done){
    for(int i = 0; i < songNums.size(); i++){
        if(number == songNums.get(i).intValue()) {number = r.nextInt(song.size()); counter++;}
    }
    if(counter == 0) done = true;
    else done = false;
}

当生成的随机数已经存在于songNums列表中时,你会生成一个新的随机数。这个新的随机数并没有和songNums列表中的所有数字进行检查。以下更改应该可以解决你的问题。

    boolean done = false;
    while(!done){
        number = r.nextInt(song.size());
        if(!songNum.contains(number)) done = true;
    }

另外,您可以使用Sasha在评论中提出的建议来打乱列表(Collections.shuffle())。


3
您现有算法的实际问题在于,当您发现已经播放过的歌曲时,没有重置计数器counter。因此,一旦出现重复,您就会陷入无限循环中- done永远不会成为真。
(实际上,它不会是无限的-一旦counter达到Integer.MAX_VALUE,它将绕回到Integer.MIN_VALUE,并最终再次达到0,因此如果您让它长时间运行,它最终会播放另一首歌曲)
这里已经有一些关于改进代码的有用建议,我不会在这里重复,但最小的更改可以修复您所拥有的问题,即将counter的初始化移动到循环内的0
boolean done = false;

while(!done){
    int counter = 0; // reset counter every time

    for(int i = 0; i < songNums.size(); i++){
        if(number == songNums.get(i).intValue()) {number = r.nextInt(song.size()); counter++;}
    }

    if(counter == 0) done = true;
    else done = false;
}

3

Sasha在评论中提到了:使用Collections.shuffle()。实际上,它看起来会像这样:

在Music类中有一个获取所有歌曲的方法:

public List<String> getSongs() {return music;}

在MusicDriver中的循环可能类似于以下内容:
List<String> songs = song.getSongs();
do{
    Collections.shuffle(songs);
    for (String songToPly: songs) {
        mp3.playMP3(song.getSong(number));
        System.out.println("Now Playing " + song.getSong(number));
        System.out.println("Enter \"Stop\" to stop playing the song");
        System.out.println("Enter \"n\" to play the next song");
        mp3.stopMP3();
        line = s.nextLine();
        if (!line.equals("n")) break;
    }
}while(line.equals("n"));

关于变量命名,将您的Music类的实例命名为"song"(单数)有点令人困惑。也许可以将其命名为"music"或至少是"songs"。


0

我会这样做:

public class MainClass() {

    public static void main(String[] args) {
        PlayerWrapper player = new PlayerWrapper();
    }
}

public class PlayerWrapper() {
    private List<MP3> playlist;
    private Scanner userInputReader;
    private String currentUserInput;

    public PlayerWrapper() {
        userInputReader = new Scanner(System.in());
        System.out.println("Filepath to playlist?");
        String playlistFileName = userInputReader.nextLine();
        playlist = PlayListExtractor.extractPlaylist(playlistFileName);
        start();
    }

    public void start() {
        playlistCopy = new ArrayList<MP3>(playlist);
        shufflePlayList(playlistCopy);
        Iterator<MP3> songIterator = playlistCopy.iterator();
        while (songIterator.hasNext()) {
            MP3 song = songIterator.next();
            songIterator.remove();
            player = new Player(song.toStream());
            player.play();
            displayCurrentSongAndCommands(song);
            currentUserInput = userInputReader.nextLine();
            if ("Stop".equals(currentUserInput )) {
                player.close();
                break;
            } else if ("n".equals(currentUserInput )) {
                player.close();
                continue;
            }
        }

        if("Stop".equals(currentUserInput)) {
            System.out.println("Playlist stopped. Press q to quit or c to continue");
            currentUserInput = userInputReader.nextLine();
            if ("q".equals(currentUserInput)) {
                System.exit(0);
            } else if ("c".equals(currentUserInput)) {
                start();
            }
        }
        start();
    }

    private void shufflePlayList(final List<MP3> playlistToBeShuffled) {
        long seed = System.nanoTime();
        Collections.shuffle(playlistToBeShuffled, new Random(seed));               
    }

    private void displayCurrentSongAndCommands(final MP3 currentSong) {
        System.out.println("Now Playing " + currentSong.toString());
        System.out.println("Enter \"Stop\" to stop playing the song");
        System.out.println("Enter \"n\" to play the next song");
    }
}

public static class PlayListExtractor() {
    private PlayListExtractor();

    public static List<MP3> extractPlayList(final String playListFileName) {
        List<MP3> result = new ArrayList<>();
        try (BufferedReader br = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = br.readLine()) != null) {
                result.add(new MP3(line));
            }
            return result;
        } catch (IOException e) {
            System.out.println("Problem parsing playlist");
        }
    }
}

public class MP3 {
    private String filename;

    public MP3(final String filename) {
        this.filename = filename;
    }

    public BufferedInputStream toStream() {
        try {
            FileInputStream fis = new FileInputStream(filename);
            return new BufferedInputStream(fis);                  
        }
        catch (Exception e) {
            System.out.println("Problem playing file " + filename);
            System.out.println(e);
        } 
    }

    public String toString() {
        return filename;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接