2010-11-08 25 views
17

Estoy usando Java 6.¿Hay alguna manera elegante de hacer algo con el último elemento de un bucle for-each en Java?

Supongamos que tengo un montón de gatos para alimentar, y supongo que myCats está ordenado.

for (Cat cat : myCats) { 

    feedDryFood(cat); 

    //if this is the last cat (my favorite), give her a tuna 
    if (...) 
     alsoFeedTuna(cat); 
} 

y yo quería tratar a mi último gato especialmente.

¿Hay alguna manera de hacerlo elegantemente dentro del circuito? La única forma en que puedo pensar es en contarlos.

Retrocediendo un poco para obtener una imagen más amplia, ¿hay algún lenguaje de programación que admita esta pequeña característica en un bucle for-each?

+0

¿Cuál es su uso real de esto? ¿Puedes tratar el primer artículo especialmente en su lugar? – starblue

+0

El uso real, no lo sé, pero definitivamente estoy aprendiendo mucho de la gente agradable e inteligente aquí. – Russell

+0

¡la cantidad de soluciones pobres con múltiples votos positivos es muy preocupante! –

Respuesta

28

Si necesita hacer esto, el mejor enfoque podría ser utilizar un iterador. Aparte de eso, debes contar. El iterador tiene un método

hasNext() 

que se puede utilizar para determinar si está en el último elemento de sus iteraciones.

EDITAR - Para aumentar la legibilidad se puede hacer algo como lo siguiente dentro del bucle basado iterador (pseudo):

Cat cat = iter.next(); 
feedDryFood(cat); 

boolean shouldGetTuna = !iter.hasNext(); 
if (shouldGetTuna) 
    alsoFeedTuna(cat) 

que es un código bastante auto-documentado a través de un uso inteligente de nombres de variables.

+2

Si bien esto sin duda responde a la pregunta, el único problema que tengo es que no hace que el requisito "el último gato sea el favorito y el atún" sea muy explícito en el código. Alguien que lea debería pensar por qué le dan atún al gato si el iterador no tiene un próximo artículo. – ColinD

+1

@colind, la legibilidad es muy importante. Actualicé mi respuesta sobre cómo la haría legible y auto documentada. – hvgotcodes

1

Lo haría fuera del circuito.

Toda la semántica de un bucle foreach es que haces lo mismo con cada objeto. En este paso, sin embargo, está tratando un objeto de manera diferente. Para mí, parece más sensato hacerlo fuera del circuito.

Además, las únicas formas que podría pensar en hacerlo en el interior son terriblemente hacky ...

-3
Cat cat; 

for (cat : myCats) { 
    feedDryFood(cat); 
} 

alsoFeedTuna(cat); 
+0

Ah, bueno, a pesar de que está fuera del circuito. – Russell

+7

-1: no compila en absoluto :) Realmente debe declararse en el alcance de foreach. El 'Iterator' es tu apuesta más elegante. – BalusC

+1

Mi mal, foreach no permite que el gato esté fuera de su alcance. – deorst

4

creo que lo mejor sería establecer un indicador en el gato, isFavoured, o tal vez un miembro estático de la clase Cat que apunta al favorito (pero de esta manera sólo puede tener un favorito). Luego solo busca el indicador cuando estés pasando por el ciclo. Después de todo, los gatos no siempre comen en el mismo orden. ;)

for (Cat cat : myCats) { 

    feedDryFood(cat); 

    if (cat.isFavoured) 
     alsoFeedTuna(cat); 
} 

Alternativamente, podría convertir a continuación, lista para una matriz y entonces sería fácil saber cuando se llega a la última - pero lo que si el último no es su favorito?

//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]); 
} 

No está claro qué es más importante: para el último gato para obtener el atún, el OR para el gato favorito para conseguir el atún. O ... ¿es el último gato el favorito, por definición de ser el último? Por favor aclara!

+2

Si eso sucede, el último se rascará en la cara. Luego, la próxima vez en el ciclo, puedes estar seguro de que será su favorito. – bzlm

+0

+1 por hacerme reír en el trabajo. – Russell

+0

Estoy de acuerdo con usted acerca de la propiedad 'isFavoured' en' Cat' o algo similar ... este es un diseño mucho más sólido que tener una posición arbitraria en una lista que indique qué gato debe obtener atún (a menos que el diseño sea realmente simplemente "el último gato tiene atún" por alguna razón). No estoy de acuerdo con un campo estático en 'Cat' para esto o con la conversión de una' Lista' a una matriz ... generalmente no hay una buena razón para hacer eso. – ColinD

9

No hay una manera clara de hacerlo dentro del bucle for-each, pero una forma directa sería simplemente usar un iterador directamente (el for-each oculta el iterador).

+6

¿Por qué se bajó este valor? – BalusC

+1

@balusc: ¡mi pregunta también! –

+3

Alguien fue de juerga downvoting –

0

¿Hay alguna razón por la que no pueda usar una construcción de bucle normal con un contador y una prueba de su posición? No es como si no usar la sintaxis foreach es poco elegante por defecto. Si utilizo el constructo foreach, entonces estaría de acuerdo con el uso del iterador.

0

y supongamos myCats se ordenan

clasificar en orden descendente de favoritismo en lugar de orden ascendente, o revertir la lista antes de la iteración

for (Cat cat in catsWithFavouriteFirst) 
{ 
    cat.feed(dryFood); 
    if (!tunaTin.isEmpty()) 
    { 
     cat.feed(tunaTin.getTunaOut()); 
    } 
} 
+0

¿Alguien quiere explicar por qué votaron una solución perfectamente razonable? – JeremyP

+0

Alguien bajó downvoted posiblemente todos los hilos. Eso apesta. De todos modos, te voté por la idea de volver a ordenar los gatos. – Russell

+0

@Russell: gracias. Noté que parecía haber un montón de votos negativos de muchas respuestas razonables. – JeremyP

4

En lo que respecta a la cuestión de si cualquier los lenguajes de programación admiten esta característica, Perl's Template Toolkit hace:

[% FOR cat IN cats; feedDryFood(cat); alsoFeedTuna(cat) IF loop.last; END %] 
+0

¡Es bueno saberlo, gracias! Esto significa que esta característica es útil para algunas personas después de todo. – Russell

26

@ solución de fbcocq

¿Cómo fue esta una mala solución? Solo agregue otra variable local.

Cat lastCat = null; 

for (Cat cat : myCats) { 
    feedDryFood(cat); 
    lastCat = cat; 
} 

alsoFeedTuna(lastCat); 

Editar: conjunto nulo primero en cuidar de los casos en que myCats no establece lastCat

+0

No creo que sea una mala solución, pero no sigue la pregunta de OP, donde preguntan si hay una forma elegante de hacerlo dentro del ciclo. –

+0

en mi humilde opinión, es bastante elegante. Funciona en unas pocas líneas sin un comportamiento especial. –

+1

¿Qué sucede cuando 'myCats' está vacío? ¿No obtendrás una excepción? – Max

8

Si se trata de un comportamiento especial que tiene que pasar al último elemento en el circuito, entonces debería tener lugar fuera el bucle y se debe proporcionar manera de dejar escapar la información del bucle:

Cat lastCat = null; 
for (Cat cat : cats) 
{ 
    // do something for each cat 
    lastCat = cat; 
} 

if (lastCat != null) 
{ 
    // do something special to last cat 
} 

recomendaría mover los bloques de estas dos afirmaciones en los métodos.

5

Use el iterador directamente.

Cat cat 
Iterator<Cat> i = myCats.iterator() 
while (i.hasNext()) 
{ 
    cat = i.next() 
    feedDryFood(cat); 
    if (!i.hasNext()) 
    { 
     alsoFeedTuna(cat); // Last cat. 
    } 
} 
0

Algo muy simple que puede hacer que lo hace explícito que el último gato es el favorito y debe conseguir el atún es conseguir simplemente el último gato por el índice y darle el atún después de dar a todos los gatos alimentos secos.

Cat favorite = myCats.get(myCats.size() - 1); 
alsoFeedTuna(favorite); 

Esta es rápido y muy bien si usted está usando un List que implementa RandomAccess tales como ArrayList. Si estás usando una estructura no RandomAccess tales como LinkedList, esto sería lento, pero se podía ver la lista como un lugar Deque y escribir:

Cat favorite = myCats.getLast(); 

Usted hace, por supuesto, tienen que asegurarse de que myCats ISN' t vacío para hacer esto.

+2

¿Por qué el voto a favor? – ColinD

+0

No he votado negativamente, pero tal vez se haya hecho porque 'myCats(). Size()' podría ser cero. – Inshallah

+0

@Inshallah: Es cierto, debe asegurarse de que 'myCats' no esté vacío. – ColinD

0
for(Iterator<Cat> iter = cats.iterator() ; iter.hasNext();) 
    { 
     Cat cat = iter.next(); 
     feedDryFood(cat); 
     if(!iter.hasNext()) 
      alsoFeedTuna(cat); 
    } 
1

la for...each construcción no es la herramienta adecuada a utilizar para obtener el comportamiento específico que está después que la cuestión está redactada, sin tener más características en el objeto Cat. El clásico Iterator está diseñado para proporcionar solo este tipo de funcionalidad. Creo que la pregunta ilustra que el diseño es defectuoso, más sobre este último.

Suponiendo que el no es una lista ordenada de gatos llamada cats. Un List que no garantiza la orden de recorrido no sería un buen candidato para la iteración, independientemente de cómo se recorre la lista.

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); 
    } 
} 

una mejor solución sería tener un método miembro en Cat. Cat.isSpecial() que devuelve un boolean.Esto sería más auto documentado y mover el comportamiento de la construcción de bucle. Entonces podría usar un constructo for...each con una prueba simple que sea autocontenida y auto documentada.

if (cat.isSpecial()) 
{ 
    this.feedTuna(cat); 
} 

creo que el "último elemento de un bucle for..each" en la pregunta es una pista falsa. Creo que el problema es más un problema de diseño que un problema de lógica de negocios, y el hecho de que el bucle for...each no puede acomodar la regla es indicativo de que debería producirse una refactorización.

Este nuevo diseño también podría acomodar múltiples gatos "especiales" sin ninguna complicación al código.

Otras soluciones más elegantes orientadas a objetos serían el Visitor Pattern o el Chain of Responsibility Pattern. El patrón de visitante resumiría la lógica de quién es el favorito del Cat y en la implementación del visitante. Lo mismo con la cadena de responsabilidad. Tenga una cadena de objetos CatFeeder y déjelos decidir si "manejarán" la alimentación o la pasarán por la cadena. De cualquier manera sería un acoplamiento más flexible y una cohesión más estrecha.

0

Sugiero no utilizar un bucle foreach y poner el método feedTuna fuera del bucle.

int i = 0; 
for(i = 0; i < cat.length; i++){ 
    feedDryFood(cat[i]); 
} 
if(cat.length != 0){ 
    feedTuna(cat[i - 1]); 
} 

Esto evita cualquier asignación o condición adicional dentro del bucle.

Cuestiones relacionadas