以下方法是用于挑选歌曲的加权随机选择算法的一部分。
我想将以下方法转换为使用流,以确定是否更清楚/更可取。我不确定这是不可能的,因为计算是有状态的操作,取决于列表中的位置。
public Song songForTicketNumber(long ticket)
{
if(ticket<0) return null;
long remaining = ticket;
for(Song s : allSongs) // allSongs is ordered list
{
rem-=s.numTickets; // numTickets is a long and never negative
if(remaining<0)
return s;
}
return null;
}
更正式的说法:如果n
是Song::numTickets
中每个Song
对象的所有allSongs
的总和,那么对于{{1} }},上述方法应返回列表中的歌曲。返回特定的0
对象n-1
的整数数量将由Song
确定。特定歌曲的选择条件是一系列连续整数,该整数由两者的x
属性和左侧列表中每个项目的x.numTickets
属性确定。按照目前的说法,超出范围的任何内容都将返回null。
注意:超出范围的行为可以修改以适应Streams(返回null除外)
答案 0 :(得分:1)
Stream
与基本for或for-each循环相比的效率是一个问题。在您的代码中,Stream
的效率很可能会比您当前的代码更低,原因如下:
BinaryOperator
进行某种匿名实现以与Stream.reduce
一起使用,这将导致比当前代码更笨重且更易读取。Stream
操作会反映出这种效率,尤其是与#1结合使用时。parallelStream
中获得一些效率,但是在这种情况下必须保持顺序将意味着效率较低。切换到Stream
可获得的唯一真正好处是内存消耗的差异(您可以将allSongs
保留在内存之外,而让Stream
处理更多的内存)效率的方法),在这里似乎并不适用。
最后,由于Stream
操作的编写会更加复杂,并且可能对您的效率造成危害(如果有的话),因此建议您不要进行此更改。
话虽这么说,但我个人无法提出一个基于Stream
的解决方案来实际回答您有关如何将此工作转换为Stream
的问题。再次,这将涉及到reducer或类似的东西,这将是复杂而奇怪的事情……(如果这还不够,我将删除此答案。)
答案 1 :(得分:0)
Java 流确实具有短路评估的功能,例如参见 findFirst()
的文档。话虽如此,递减和检查 remaining
需要状态突变,这不是很好。不是很好,但可行:
public Optional<Song> songForTicketNumber(long ticket, Stream<Song> songs) {
if (ticket < 0) return Optional.empty();
AtomicLong remaining = new AtomicLong(ticket);
return songs.filter(song -> decrementAndCheck(song, remaining)).findFirst();
}
private boolean decrementAndCheck(Song song, AtomicLong total) {
total.addAndGet(-song.numTickets);
return total.get() < 0;
}
据我所知,这种方法的唯一优点是您可以根据需要切换到并行流。