Есть ли элегантный способ сделать что-то для последнего элемента цикла for-each в Java?
Я использую Java 6.
Предположим, что у меня была куча кошек, чтобы их кормить, и предположим, что myCats отсортированы.
for (Cat cat : myCats) {
feedDryFood(cat);
//if this is the last cat (my favorite), give her a tuna
if (...)
alsoFeedTuna(cat);
}
и я хотел обработать моего последнего кота специально.
Есть ли способ сделать это элегантно внутри цикла? Единственный способ, с помощью которого я могу думать, - считать их.
Отступив немного назад для более широкого изображения, есть ли какой-либо язык программирования, который поддерживает эту небольшую функцию в каждом цикле?
Ответы
Ответ 1
Если вам нужно это сделать, лучшим вариантом может быть использование Iterator. Помимо этого, вы должны учитывать. Итератор имеет
hasNext()
который вы можете использовать, чтобы определить, находитесь ли вы в последнем элементе своих итераций.
EDIT. Чтобы повысить читаемость, вы можете сделать что-то вроде следующего в цикле, основанном на Iterator (psuedo):
Cat cat = iter.next();
feedDryFood(cat);
boolean shouldGetTuna = !iter.hasNext();
if (shouldGetTuna)
alsoFeedTuna(cat)
что является довольно самодокументирующим кодом с помощью умного использования имен переменных.
Ответ 2
Решение @fbcocq
Как это плохое решение? Просто добавьте еще одну локальную переменную.
Cat lastCat = null;
for (Cat cat : myCats) {
feedDryFood(cat);
lastCat = cat;
}
alsoFeedTuna(lastCat);
Изменить: сначала установите null, чтобы позаботиться о случаях, когда myCats не устанавливает lastCat
Ответ 3
Нет четкого способа сделать это в цикле for-each, но простым способом было бы просто использовать итератор напрямую (для каждого скрывает итератор).
Ответ 4
Если это особое поведение, которое должно произойти с последним элементом цикла, то оно должно происходить вне цикла, и вы должны предоставить способ, чтобы информация выходила из цикла:
Cat lastCat = null;
for (Cat cat : cats)
{
// do something for each cat
lastCat = cat;
}
if (lastCat != null)
{
// do something special to last cat
}
Я бы рекомендовал переместить блоки этих двух операторов в методы.
Ответ 5
Используйте итератор напрямую.
Cat cat
Iterator<Cat> i = myCats.iterator()
while (i.hasNext())
{
cat = i.next()
feedDryFood(cat);
if (!i.hasNext())
{
alsoFeedTuna(cat); // Last cat.
}
}
Ответ 6
Я думаю, лучше всего установить индикатор на cat, 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]);
}
Неясно, что важнее: для последней кошки, чтобы получить тунца, ИЛИ для любимой кошки, чтобы получить тунца. Или... последний котик любимый, по определению будучи последним? Просьба уточнить!
Ответ 7
Что касается вопроса о том, поддерживают ли какие-либо языки программирования эту функцию, Perl Template Toolkit делает:
[% FOR cat IN cats; feedDryFood(cat); alsoFeedTuna(cat) IF loop.last; END %]
Ответ 8
Я бы сделал это за пределами цикла.
Вся семантика цикла foreach заключается в том, что вы делаете одно и то же для каждого объекта. На этом этапе, хотя вы рассматриваете один объект по-разному. Мне кажется более разумным делать это за пределами цикла.
Плюс, единственные способы, которые я мог придумать, чтобы сделать это внутри, ужасно хакерские...
Ответ 9
Конструкция for...each
не является надлежащим инструментом для использования, чтобы получить конкретное поведение, которое вы выполняете после формулировки вопроса, не имея дополнительных функций в объекте Cat
. Классический Iterator
предназначен для обеспечения такого рода функциональности. Я думаю, что этот вопрос иллюстрирует, что дизайн ошибочен, больше на этом последнем.
Предполагая, что существует отсортированный список кошек под названием cats
. A 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()
, который возвращает a boolean
. Это было бы более самодокументируемо и вывело бы поведение из конструкции цикла. Затем вы можете использовать конструкцию for...each
с простым тестом, который является самодостаточным и самодокументированным.
if (cat.isSpecial())
{
this.feedTuna(cat);
}
Я думаю, что "последний элемент цикла for..each
" в вопросе - это красная селедка. Я думаю, что проблема скорее проблема дизайна, чем проблема бизнес-логики, и тот факт, что цикл for...each
не может принять это правило, указывает на необходимость рефакторинга.
В этом новом дизайне также будут размещаться несколько "специальных" кошек без каких-либо осложнений для кода.
Другими более элегантными объектно-ориентированными решениями будут шаблон посетителя или Цепь ответственности.. Шаблон посетителя должен абстрагировать логику того, кто из фаворитов из Cat
и в реализацию посетителя. То же самое с цепочкой ответственности. Имейте цепочку объектов CatFeeder
, и пусть они решат, нужно ли "обрабатывать" кормление или передавать его по цепочке. В любом случае это будет ослабление сцепления и более тесная сплоченность.
Ответ 10
Есть ли причина, по которой вы не можете использовать обычную петлевую конструкцию со счетчиком и тест для своей позиции? Это не похоже на то, что по умолчанию не используется синтаксис foreach. Если вы используете конструкцию foreach, я бы согласился использовать итератор.
Ответ 11
и предположим, что myCats отсортировано
Сортируйте его в порядке убывания фаворитизма вместо восходящего или отмените список перед повторением
for (Cat cat in catsWithFavouriteFirst)
{
cat.feed(dryFood);
if (!tunaTin.isEmpty())
{
cat.feed(tunaTin.getTunaOut());
}
}
Ответ 12
Что-то очень простое, что вы можете сделать, что делает его явным, что последний кот является фаворитом и должен получить тунца, чтобы просто получить последнего кота по индексу и дать ему тунца после того, как дал всем кошкам сухую пищу.
Cat favorite = myCats.get(myCats.size() - 1);
alsoFeedTuna(favorite);
Это быстро и просто отлично, если вы используете List
, который реализует RandomAccess
, например ArrayList
. Если вы используете структуру не RandomAccess
, такую как LinkedList
, это будет медленным, но вы можете просмотреть список как Deque
и написать:
Cat favorite = myCats.getLast();
Конечно, вам нужно убедиться, что myCats
не пуст для этого.
Ответ 13
for( Iterator<Cat> iter = cats.iterator() ; iter.hasNext(); )
{
Cat cat = iter.next();
feedDryFood(cat);
if(!iter.hasNext())
alsoFeedTuna(cat);
}
Ответ 14
Я предлагаю не использовать цикл foreach и помещать метод feedTuna за пределы цикла.
int i = 0;
for( i = 0; i < cat.length; i++ ){
feedDryFood(cat[i]);
}
if( cat.length != 0 ){
feedTuna(cat[i - 1]);
}
Это позволяет избежать любых дополнительных назначений или условных выражений внутри цикла.
Ответ 15
Cat cat;
for (cat : myCats) {
feedDryFood(cat);
}
alsoFeedTuna(cat);