Подтвердить что ты не робот

Есть ли элегантный способ сделать что-то для последнего элемента цикла 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);
}

и я хотел обработать моего последнего кота специально.

Есть ли способ сделать это элегантно внутри цикла? Единственный способ, с помощью которого я могу думать, - считать их.

Отступив немного назад для более широкого изображения, есть ли какой-либо язык программирования, который поддерживает эту небольшую функцию в каждом цикле?

4b9b3361

Ответ 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);