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
-objekt känner jag till följande sätt att slinga genom alla element:
// 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.
for (E element : list) {
// 1 - can call methods of element
// ...
}
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
// ...
}
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
// ...
}
list.stream().map(e -> e + 1); // Can apply a transformation function for e
(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:
import java.util.*;
public class ListIterationExample {
public static void main(String []args){
List<Integer> numbers = new ArrayList<Integer>();
// populates list with initial values
for (Integer i : Arrays.asList(0,1,2,3,4,5,6,7))
numbers.add(i);
printList(numbers); // 0,1,2,3,4,5,6,7
// replaces each element with twice its value
for (int index=0; index < numbers.size(); index++) {
numbers.set(index, numbers.get(index)*2);
}
printList(numbers); // 0,2,4,6,8,10,12,14
// does nothing because list is not being changed
for (Integer number : numbers) {
number++; // number = new Integer(number+1);
}
printList(numbers); // 0,2,4,6,8,10,12,14
// same as above -- just different syntax
for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
Integer number = iter.next();
number++;
}
printList(numbers); // 0,2,4,6,8,10,12,14
// ListIterator<?> provides an "add" method to insert elements
// between the current element and the cursor
for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
Integer number = iter.next();
iter.add(number+1); // insert a number right before this
}
printList(numbers); // 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
// Iterator<?> provides a "remove" method to delete elements
// between the current element and the cursor
for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
Integer number = iter.next();
if (number % 2 == 0) // if number is even
iter.remove(); // remove it from the collection
}
printList(numbers); // 1,3,5,7,9,11,13,15
// ListIterator<?> provides a "set" method to replace elements
for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
Integer number = iter.next();
iter.set(number/2); // divide each element by 2
}
printList(numbers); // 0,1,2,3,4,5,6,7
}
public static void printList(List<Integer> numbers) {
StringBuilder sb = new StringBuilder();
for (Integer number : numbers) {
sb.append(number);
sb.append(",");
}
sb.deleteCharAt(sb.length()-1); // remove trailing comma
System.out.println(sb.toString());
}
}
Jag vet inte vad du anser vara patologiskt, men låt mig ge dig några alternativ som du kanske inte har sett tidigare:
List<E> sl= list ;
while( ! sl.empty() ) {
E element= sl.get(0) ;
.....
sl= sl.subList(1,sl.size());
}
Eller dess rekursiva version:
void visit(List<E> list) {
if( list.isEmpty() ) return;
E element= list.get(0) ;
....
visit(list.subList(1,list.size()));
}
Även en rekursiv version av det klassiska "for(int i=0...` :
void visit(List<E> list,int pos) {
if( pos >= list.size() ) return;
E element= list.get(pos) ;
....
visit(list,pos+1);
}
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:
int i = 0;
do{
E element = list.get(i);
i++;
}
while (i < list.size());
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.