Sätt att iterera över en lista i Java
Eftersom jag är ganska ny i Java-språket försöker jag bekanta mig med alla sätt (eller åtminstone de icke-patologiska) som man kan iterera genom en lista (eller kanske andra samlingar) och fördelarna eller nackdelarna med varje sätt.
Givet ett List list
-objekt känner jag till följande sätt att slinga genom alla element:
Basic for loop (det finns naturligtvis även motsvarande "while"- och "do while"-slingor).
// Not recommended (see below)!
for (int i = 0; i < list.size(); i++) {
E element = list.get(i);
// 1 - can call methods of element
// 2 - can use 'i' to make index-based calls to methods of list
// ...
}
Anmärkning: Som @amarseillan påpekade är denna form ett dåligt val.
för att iterera över List
s, eftersom den faktiska implementeringen av
metoden get
kanske inte är lika effektiv som när man använder en Iterator
.
Till exempel måste implementeringar av LinkedList
gå igenom hela
element som föregår i för att få fram det i:e elementet.
I exemplet ovan finns det inget sätt för List
-implementationen att
"spara sin plats" för att göra framtida iterationer mer effektiva.
För en ArrayList
spelar det egentligen ingen roll, eftersom komplexiteten/kostnaden för get
är konstant tid (O(1)) medan den för en LinkedList
är proportionell mot listans storlek (O(n)).
Mer information om beräkningskomplexiteten hos de inbyggda Collections
-implementationerna finns i denna fråga.
Förbättrad [for-slinga][for-each-slinga] (förklaras tydligt [i denna fråga][for-each-slinga-so])
for (E element : list) {
// 1 - can call methods of element
// ...
}
Iteratoriterator
for (Iterator<E> iter = list.iterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// ...
}
ListIterator
for (ListIterator<E> iter = list.listIterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// 3 - can use iter.add(...) to insert a new element into the list
// between element and iter->next()
// 4 - can use iter.set(...) to replace the current element
// ...
}
Functional Java
list.stream().map(e -> e + 1); // Can apply a transformation function for e
Iterable.forEach, Stream.forEach, ...
(En map-metod från Java 8's Stream API (se @i_am_zero's svar).)
I Java 8 har samlingsklasser som implementerar Iterable
(till exempel alla List
s) nu en forEach
-metod, som kan användas istället för for loop statement som demonstreras ovan. (Här finns en annan fråga som ger en bra jämförelse.)
Arrays.asList(1,2,3,4).forEach(System.out::println);
// 1 - can call methods of an element
// 2 - would need reference to containing object to remove an item
// (TODO: someone please confirm / deny this)
// 3 - functionally separates iteration from the action
// being performed with each item.
Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
// Same capabilities as above plus potentially greater
// utilization of parallelism
// (caution: consequently, order of execution is not guaranteed,
// see [Stream.forEachOrdered][stream-foreach-ordered] for more
// information about this).
Vilka andra sätt finns det, om det finns några?
(Mitt intresse beror för övrigt inte alls på en önskan att [optimera prestanda][iterator-performance-question]; jag vill bara veta vilka former som är tillgängliga för mig som utvecklare.)
Exempel på varje typ som anges i frågan:
ListIterationExample.java
Jag vet inte vad du anser vara patologiskt, men låt mig ge dig några alternativ som du kanske inte har sett tidigare:
Eller dess rekursiva version:
Även en rekursiv version av det klassiska "for(int i=0...` :
Jag nämner dem eftersom du är "något ny i Java" och detta kan vara intressant.
Du kan alltid byta ut det första och tredje exemplet mot en while-slinga och lite mer kod. Detta ger dig fördelen att kunna använda do-while:
Naturligtvis kan den här typen av sak orsaka ett NullPointerException om list.size() returnerar 0, eftersom den alltid utförs minst en gång. Detta kan åtgärdas genom att testa om elementet är null innan dess attribut/metoder används tho. Det är ändå mycket enklare och lättare att använda for-slingan.