【问题标题】:Playlist shuffle on/off播放列表随机播放开/关
【发布时间】:2011-01-14 16:04:59
【问题描述】:

我正在用java编程,但我也可以采用C++(甚至伪)代码,没问题。这是我现在的情况:

我有一个类似播放列表的东西,例如List<MyPlayListItem> lsMyPlaylist。现在我想让用户有机会洗牌,然后再回到有序列表。我的意思是,假设用户处于“随机播放模式”,播放器例如从第 7 首歌曲跳到第 5 首歌曲,但随后用户关闭了“随机播放模式”,因为他接下来想听第 6 首歌曲。您将如何解决这个问题?

我有几个想法:

  • 使用两个列表,一个是原始的,一个是随机的(存储太多)
  • 有一个 int 列表,我会对其进行洗牌,然后用作索引来获取元素(可能会更好一点)
  • 使用哈希表(解决方案?不过,我可能需要一些建议)

哦,这不是家庭作业(我希望我再次回到那个年龄:-D)。

编辑:

我刚刚完成了一个这样的实现:

PlayList<E> implements List {

   private List<E> lsObjs = null;
   private List<Integer> lsIdxs = null;

   boolean bShuffleMode = false;
   int Pos = 0;
}

但是现在,我正在考虑类似的事情:

PlayListItem<E> {

   int iNextItem = 0;

}

PlayList<PlayListItem> implements List {

   private List<PlayListItem> lsObjs = null;

   boolean bShuffleMode = false;
   int Pos = 0;

}

对此不确定...可能仍需要建议。如果我指定列表中的对象,我什至可以实现列表吗?嗯……

【问题讨论】:

  • +1 表示“我希望我再次回到那个年龄”。难道我们都没有!

标签: java c++ list shuffle


【解决方案1】:

我假设您的移动设备没有几个 K 可用。拥有两个列表不会重复列表中更大的元素。

将原始索引作为字段添加到 MyPlayListItem。将它们改组后,您可以使用比较器在索引上对它们进行排序,以将它们恢复为原始顺序。注意:除非索引小于 4 字节,否则这将使用与拥有两个列表一样多的内存。

在 32 位系统上,int[] 和 List 消耗的内存量几乎相同。 (大约 16 个字节的差异)

【讨论】:

  • 谢谢彼得。我可能在各种情况下都需要这个算法,这就是为什么我希望它做得很好。哦,我已经知道我将在电话应用程序中使用它——事实上。感谢您的建议,这也是我认为的...但我想知道哈希表是否会做同样的事情,这就是为什么我有点犹豫...
  • 一个 HashMap 或 HashTable 将比拥有两个 ArraysList 贵得多。 (包含不同顺序的相同对象)
  • 我重读了您的回答以及上述评论。到目前为止,我发现您的回答最有帮助,尤其是您对记忆的评论。谢谢。
【解决方案2】:

我建议您为歌曲列表(库)使用一个主容器,每个播放列表都有一个容器。当然,播放列表应该指向库中的元素。

在那之后,有许多随机播放的方法,我最喜欢的一种方法是在一个容器中拥有一个歌曲列表,您可以在其中随机挑选歌曲并删除挑选的歌曲。所以它将是随机的,但不重复播放。

我已经很久没有用 Java 编程了,所以我将给出一个 C++ 示例。我希望它简单明了:

// --*-- C++ --*--

#include <ctime>
#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <vector>
#include <list>

struct Song
{
    std::string name;

    Song (const std::string & name) :
        name (name)
    {
    }

    void play ()
    {
        printf ("Playing '%s'...\n", name.c_str ());
    }
};

typedef std::vector<Song> Library;
typedef std::vector<Song *> Playlist;
typedef std::vector<size_t> SongIDSet;

int
main ()
{
    srand (time (NULL));

    Library library;

    library.push_back (Song ("Lady Gaga - Bad Romance"));
    library.push_back (Song ("Rihanna - Only Girl"));
    library.push_back (Song ("Nelly - Just a Dream"));
    library.push_back (Song ("Animal - Neon Trees"));
    library.push_back (Song ("Eminem ft. Rihanna - Love The Way You Lie"));

    Playlist playlist;

    for (Library::iterator it = library.begin (),
             end_it = library.end (); it != end_it; ++it)
    {
        printf ("Added song -> %s\n", it->name.c_str ());
        playlist.push_back (&(*it));
    }

    SongIDSet shuffle;
    for (size_t i = 0, end_i = playlist.size (); i < end_i; ++i)
    {
        shuffle.push_back (i);
    }

    size_t nowPlaying = 0;

    while (!shuffle.empty ())
    {
        size_t songIndex = 0;
            printf ("Shuffle? [Y/n] ");
        switch (fgetc (stdin))
        {
            case 'N':
            case 'n':
                songIndex = nowPlaying + 1;
                fgetc (stdin); // Skip newline.
                break;
            case 'Y':
            case 'y':
                fgetc (stdin); // Skip newline.
            default:
            {
                printf ("Shuffling...\n");
                size_t index = rand () % shuffle.size ();
                assert (index >= 0);
                assert (index < shuffle.size ());
                songIndex = shuffle[index];
                shuffle.erase (shuffle.begin () + index);
            }
        }
        assert (songIndex >= 0);
        if (songIndex < playlist.size ())
        {
            nowPlaying = songIndex;
            playlist[nowPlaying]->play ();
        }
        else
        {
            break; // Done playing.. Repeat maybe?
        }
    }
}

这是一个运行/输出示例:

$ ./test 
Added song -> Lady Gaga - Bad Romance
Added song -> Rihanna - Only Girl
Added song -> Nelly - Just a Dream
Added song -> Animal - Neon Trees
Added song -> Eminem ft. Rihanna - Love The Way You Lie
Shuffle? [Y/n] 
Shuffling...
Playing 'Eminem ft. Rihanna - Love The Way You Lie'...
Shuffle? [Y/n] 
Shuffling...
Playing 'Nelly - Just a Dream'...
Shuffle? [Y/n] 
Shuffling...
Playing 'Rihanna - Only Girl'...
Shuffle? [Y/n] 
Shuffling...
Playing 'Animal - Neon Trees'...
Shuffle? [Y/n] 
Shuffling...
Playing 'Lady Gaga - Bad Romance'...
$ ./test 
Added song -> Lady Gaga - Bad Romance
Added song -> Rihanna - Only Girl
Added song -> Nelly - Just a Dream
Added song -> Animal - Neon Trees
Added song -> Eminem ft. Rihanna - Love The Way You Lie
Shuffle? [Y/n] 
S    huffling...
Playing 'Nelly - Just a Dream'...
Shuffle? [Y/n] n
Playing 'Animal - Neon Trees'...
Shuffle? [Y/n] n
Playing 'Eminem ft. Rihanna - Love The Way You Lie'...
Shuffle? [Y/n] n

【讨论】:

  • 谢谢。已建议您的解决方案。但是令人印象深刻的是,您如何及时(+1)将所有内容放入代码中。我喜欢你的 C++“风格”。为什么你将 Song 声明为一个结构而不是一个类呢?只有当“事物”没有成员函数时,我才会使用结构。你的约定是什么?
  • @AudioDroid:我使用 struct 只是为了简单,而不是指定访问级别,也不是创建 getter/setter 等。当然在现实生活中 Song 应该是一个类。这段代码中唯一的技术问题是从std::vector 中删除非最后一个元素不是很有效。使用一些随机访问容器会更好。
【解决方案3】:

如何在MyPlayListItem 上添加 nextSong 属性,该属性将引用原始播放列表中的下一首歌曲。每次用户洗牌播放列表时,列表将被洗牌,但将保留原始播放列表。但是,您当然需要一些东西来引用原始播放列表中的第一个 MyPlayListItem

【讨论】:

  • 这也是个好主意。但是我认为拥有一个模板会很好:PlayList 实现 List {...}。所以我可以把任何类型的对象放在列表中。这是一件好事。然后你的方法不起作用。或者您是否也看到了解决方案? ...也许是 PlayList>?嗯……
【解决方案4】:

如果内存是您最大的限制,您可以随机选择一首歌曲在每首歌曲的结尾播放。这显然意味着您不必在重新开始播放列表之前播放每首歌曲。 :)

如果您需要在再次开始播放歌曲之前播放每首歌曲,则存储一个长度等于歌曲数量的位串。当您播放歌曲时,设置与该索引对应的位。如果设置了位,则永远不要播放歌曲,而是在此时增加索引,直到您在未设置的字符串中找到位。如果字符串中的所有位都已设置,则重置位串。

【讨论】:

  • 哦,哇,我从来没有想过这个想法。谢谢你分享。事实上,我想在重新开始之前播放每一首歌。
  • 嗯。我试图在内存与计算能力之间找到“中间”的东西。 seggy 指出了您的解决方案的缺点。但我喜欢你只是增加的想法,如果位已经设置。尽管如此,如果您有 100 首或更多歌曲,并且假设只有第 1 首歌曲没有播放,并且您碰巧运气不好并且随机生成器返回“2”,那么您将在整个列表中递增。 :-((我认为你没有意识到这一点。只是想向周围的人指出这一点。)
  • 绝对正确,这是一个折衷方案,它只需要很少的内存。我猜您使用的是 android 设备,很可能基于 java 数据结构的解决方案会更合适。位串方法更适合嵌入式或类似的东西。
【解决方案5】:

那么简单的东西呢?

使用指向列表的索引指针,比如一个整数。这是当前正在播放的歌曲。当播放器处于随机播放模式时,此指针被设置为介于 0 和列表中项目数 - 1 之间的随机数。当播放器处于顺序播放模式时,此指针简单地递增。

如果你想防止歌曲重复,有两种策略。一种是使用位串,正如上面另一个人所建议的那样。另一种是使用一套。每次设置指针时,检查集合中的成员。如果它已经在集合中,请获取另一个索引。如果不是,则添加它并播放歌曲。两者都可以达到相同的效果。

当播放歌曲与未播放歌曲的比例变高且随机索引始终在列表中时,您会遇到问题。

【讨论】:

  • 这是一个好主意,但实际上 - 正如所指出的 - 我不喜欢我的随机发生器会非常忙碌的事实 - 假设 - 100首歌曲中只有一首(或更多)左。
  • 另一种方法是颠倒这个逻辑。创建一个列表,最初包含从 0 到歌曲列表长度 - 1 的所有索引。索引指针的设置方式与原始帖子相同,但不是索引到原始歌曲列表,而是索引到索引列表.选择索引(通过Shuffle或顺序模式)时,索引处的数字用于引用列表中的歌曲,然后删除。这解决了我在帖子底部提到的问题。
  • 是的,这与彼得建议的差不多。我想我会去的。如果我有单独的索引列表,我可以有任何类型的列表。我什至在考虑编写自己的容器/模板类...
【解决方案6】:

你可以有两个项目:
1) 列表中的真实项目列表
2) 具有整数索引的向量

当随机播放被激活时,你会随机播放索引。当它关闭时,对索引进行排序。访问列表中的元素时,请使用索引。

类似这样的:

std::list< MyListElement > elements = ...;
std::vector< int > indexes = ...;

// shuffle off
sort( indexes.begin(), indexes.end() );

// get elements
elements[ indexes[ i ] ];

【讨论】:

    猜你喜欢
    • 2023-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-02
    • 1970-01-01
    • 1970-01-01
    • 2010-12-21
    相关资源
    最近更新 更多