At være noget nyt til Java-sproget I'm forsøger at gøre mig bekendt med alle de måder (eller i det mindste de ikke-patologiske dem), at man kan iterere gennem en liste (eller måske andre samlinger) og fordele eller ulemper ved hver.
Givet et List
objekt, kender jeg til følgende måder at løbe gennem alle elementer på:
while
/ do while
loops)// 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
// ...
}
Bemærk: Som @amarseillan påpegede, er denne form et dårligt valg
til iterering over List
s, fordi den faktiske implementering af
get
-metoden er muligvis ikke så effektiv som ved brug af en Iterator
.
F.eks. skal LinkedList
-implementeringer gennemløbe hele
elementerne forud for i for at få fat i-te element.
I ovenstående eksempel er der ingen mulighed for, at List
-implementeringen kan
"gemme sin plads" for at gøre fremtidige iterationer mere effektive.
For en ArrayList
betyder det ikke rigtig noget, fordi kompleksiteten/omkostningen af get
er konstant tid (O(1)), mens den for en LinkedList
er proportional med listens størrelse (O(n)).
For flere oplysninger om den beregningsmæssige kompleksitet af de indbyggede Collections
-implementeringer, se dette spørgsmål.
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-metode fra Java 8's Stream API (se @i_am_zero's svar).)
I Java 8 har opsamlingsklasser, der implementerer Iterable
(f.eks. alle List
s) nu en forEach
-metode, som kan bruges i stedet for den for loop statement, der er demonstreret ovenfor. (Her er et andet spørgsmål, der giver en god sammenligning).
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).
Hvilke andre måder er der evt.?
(BTW, min interesse skyldes slet ikke et ønske om at optimere ydeevnen; jeg vil bare gerne vide, hvilke former der er tilgængelige for mig som udvikler).
Eksempel på hver af de typer, der er nævnt i spørgsmålet:
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());
}
}
Jeg ved ikke, hvad du betragter som patologisk, men lad mig give dig nogle alternativer, som du måske ikke har set før:
List<E> sl= list ;
while( ! sl.empty() ) {
E element= sl.get(0) ;
.....
sl= sl.subList(1,sl.size());
}
Eller dens rekursive version:
void visit(List<E> list) {
if( list.isEmpty() ) return;
E element= list.get(0) ;
....
visit(list.subList(1,list.size()));
}
Også en rekursiv version af den klassiske 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);
}
Jeg nævner dem, fordi du er "noget ny i Java" og dette kunne være interessant.
Du kan altid udskifte det første og tredje eksempel med en while-loop og lidt mere kode. Det giver dig den fordel, at du kan bruge do-while:
int i = 0;
do{
E element = list.get(i);
i++;
}
while (i < list.size());
Selvfølgelig kan denne slags ting forårsage en NullPointerException hvis list.size() returnerer 0, fordi den altid bliver udført mindst én gang. Dette kan løses ved at teste om elementet er null før du bruger dets attributter/metoder tho. Alligevel er det meget enklere og nemmere at bruge for loop'en