我正在使用Java 6.
假设我有一堆猫要喂,并假设myCats已经分类。
for (Cat cat : myCats) {
feedDryFood(cat);
//if this is the last cat (my favorite), give her a tuna
if (...)
alsoFeedTuna(cat);
}
我想特别对待我的最后一只猫。
有没有办法在循环中优雅地做到这一点?我能想到的唯一方法是计算它们。
稍微退一步以获得更广泛的图片,是否有任何编程语言支持for-each循环中的这个小功能?
答案 0 :(得分:28)
如果您需要这样做,最好的方法可能是使用Iterator。除此之外,你必须要数。迭代器有一个
hasNext()
可用于确定您是否在迭代的最后一项上的方法。
编辑 - 为了提高可读性,您可以在基于迭代器的循环(psuedo)中执行以下操作:
Cat cat = iter.next();
feedDryFood(cat);
boolean shouldGetTuna = !iter.hasNext();
if (shouldGetTuna)
alsoFeedTuna(cat)
通过巧妙使用变量名称,这是相当自我记录的代码。
答案 1 :(得分:26)
@ fbcocq的解决方案
这是一个糟糕的解决方案?只需添加另一个局部变量。
Cat lastCat = null;
for (Cat cat : myCats) {
feedDryFood(cat);
lastCat = cat;
}
alsoFeedTuna(lastCat);
编辑:首先设置null以处理myCats未设置lastCat的情况
答案 2 :(得分:9)
在for-each循环中没有明确的方法可以做到这一点,但直接的方法是直接使用迭代器(for-each隐藏迭代器)。
答案 3 :(得分:8)
如果这是一个必须发生在循环中最后一项的特殊行为,那么它应该发生在循环之外,你应该提供让信息逃脱循环的方法:
Cat lastCat = null;
for (Cat cat : cats)
{
// do something for each cat
lastCat = cat;
}
if (lastCat != null)
{
// do something special to last cat
}
我建议将这两个语句的块移动到方法中。
答案 4 :(得分:5)
直接使用迭代器。
Cat cat
Iterator<Cat> i = myCats.iterator()
while (i.hasNext())
{
cat = i.next()
feedDryFood(cat);
if (!i.hasNext())
{
alsoFeedTuna(cat); // Last cat.
}
}
答案 5 :(得分:4)
我认为最好的办法是在猫isFavoured
上设置指标,或者Cat
类的静态成员指向最喜欢的(但这样你只能拥有最喜欢的一个)。然后只需在循环中查找指标。毕竟,猫并不总是以相同的顺序进食。 ;)
for (Cat cat : myCats) {
feedDryFood(cat);
if (cat.isFavoured)
alsoFeedTuna(cat);
}
或者,您可以将列表转换为数组然后很容易知道何时到达最后一个 - 但如果最后一个不是您最喜欢的那个怎么办?
//only a rough idea, may not compile / run perfectly
catArray = cats.toArray(cats);
for (int i = 0 ; i < catArray.length(); i++){
feedDryFood(catArray [i]);
//check for last cat.
if (i == catArray.length()-1 )
alsoFeedTuna(catArray [i]);
}
目前尚不清楚哪个更重要:最后一只猫可以获得金枪鱼,或者最喜欢的猫可以获得金枪鱼。或者......最后一只猫是最喜欢的,根据最后的定义?请澄清!
答案 6 :(得分:4)
关于任何编程语言是否支持此功能的问题,Perl的Template Toolkit会这样做:
[% FOR cat IN cats; feedDryFood(cat); alsoFeedTuna(cat) IF loop.last; END %]
答案 7 :(得分:1)
我会在循环之外做。
foreach循环的整个语义是你对每个对象做同样的事情。在此步骤中,您将以不同方式处理一个对象。对我来说,在循环之外做这件事似乎更明智。
另外,我内心想到的唯一方法是可怕的hacky ......
答案 8 :(得分:1)
for...each
构造不是正确的工具,可用于获取您在问题措辞时所遵循的特定行为,而Cat
对象中没有更多功能。经典Iterator
旨在提供此类功能。我认为这个问题说明了设计存在缺陷,更多的是后者。
假设有一个名为cats
的猫的排序列表。无论遍历列表如何遍历,不保证遍历顺序的List
都不适合迭代。
final Iterator<Cat> iterator = cats.iterator();
while (iterator.hasNext())
{
final Cat cat = iterator.next();
this.feedDryCatFood(cat);
// special case, if there are no more cats in the list
// feed the last one tuna as well.
if (!iterator.hasNext())
{
this.alsoFeedTuna(cat);
}
}
更好的解决方案是在Cat
上使用成员方法。 Cat.isSpecial()
返回boolean
。这将更加自我记录并将行为移出循环结构。然后,您可以使用for...each
构造,其中包含一个自包含且自我记录的简单测试。
if (cat.isSpecial())
{
this.feedTuna(cat);
}
我认为问题中的“for..each
循环的最后一个元素”是一个红色的鲱鱼。我认为问题更多的是设计问题而不是业务逻辑问题,而for...each
循环无法容纳规则的事实表明应该进行重构。
这种新设计还可以容纳多只“特殊”猫,而且代码没有任何复杂性。
其他更优雅的面向对象解决方案是Visitor Pattern或Chain of Responsibility Pattern。访问者模式将从Cat
和访问者实现中抽象出最喜欢的人的逻辑。与责任链相同。有一串CatFeeder
个物品,让他们决定是否“处理”喂食或将其传递到链条上。无论哪种方式都是松散的耦合和更紧密的凝聚力。
答案 9 :(得分:0)
是否有任何理由不能使用带有计数器和测试位置的普通循环结构?它不像默认情况下不使用foreach语法是不优雅的。如果使用foreach构造,那么我同意使用迭代器。
答案 10 :(得分:0)
并假设myCats已排序
按偏袒而不是升序排序,或者在迭代前反转列表
for (Cat cat in catsWithFavouriteFirst)
{
cat.feed(dryFood);
if (!tunaTin.isEmpty())
{
cat.feed(tunaTin.getTunaOut());
}
}
答案 11 :(得分:0)
你可以做的非常简单明确表示最后一只猫是最喜欢的并且应该得到金枪鱼只是通过索引获得最后一只猫并在给予所有猫干粮后给它金枪鱼。
Cat favorite = myCats.get(myCats.size() - 1);
alsoFeedTuna(favorite);
如果您使用List
来实现RandomAccess
,例如ArrayList
,那么这很快就会很好。如果您使用的是非RandomAccess
结构,例如LinkedList
,这会很慢,但您可以将列表视为Deque
并写入:
Cat favorite = myCats.getLast();
您当然需要确保myCats
不为空。
答案 12 :(得分:0)
for( Iterator<Cat> iter = cats.iterator() ; iter.hasNext(); )
{
Cat cat = iter.next();
feedDryFood(cat);
if(!iter.hasNext())
alsoFeedTuna(cat);
}
答案 13 :(得分:0)
我建议不要使用foreach循环并将feedTuna方法放在循环之外。
int i = 0;
for( i = 0; i < cat.length; i++ ){
feedDryFood(cat[i]);
}
if( cat.length != 0 ){
feedTuna(cat[i - 1]);
}
这可以避免循环中的任何其他赋值或条件。
答案 14 :(得分:-3)
Cat cat;
for (cat : myCats) {
feedDryFood(cat);
}
alsoFeedTuna(cat);