Omdat ik een beetje nieuw ben in de Java taal probeer ik vertrouwd te raken met alle manieren (of tenminste de niet-pathologische) waarop men door een lijst (of misschien andere verzamelingen) zou kunnen itereren en de voor- of nadelen van elk.
Gegeven een List
object, ken ik de volgende manieren om door alle elementen te lopen:
while
/ do while
lussen)// 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
// ...
}
Opmerking: Zoals @amarseillan al aangaf, is deze vorm een slechte keuze
voor het itereren over Lijsten
, omdat de werkelijke implementatie van
de get
methode niet zo efficiënt kan zijn als bij gebruik van een Iterator
.
Bijvoorbeeld, LinkedList
implementaties moeten door alle
elementen die voor i staan om het i-de element te krijgen.
In het bovenstaande voorbeeld is er geen manier voor de List
implementatie om
"zijn plaats op te slaan" om toekomstige iteraties efficiënter te maken.
Voor een ArrayList
maakt het niet echt uit, omdat de complexiteit/kosten van get
constante tijd is (O(1)) terwijl het voor een LinkedList
evenredig is met de grootte van de lijst (O(n)).
Voor meer informatie over de rekenkundige complexiteit van de ingebouwde Collections
implementaties, kijk bij deze vraag.
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
(Een map methode uit Java 8's Stream API (zie @i_am_zero's antwoord).
In Java 8 hebben verzamelingsklassen die Iterable
implementeren (bijvoorbeeld alle List
s) nu een forEach
methode, die gebruikt kan worden in plaats van de hierboven gedemonstreerde for-loop statement. (Hier is [een andere vraag][java-8-foreach-comparison] die een goede vergelijking geeft).
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).
Welke andere manieren zijn er, als die er zijn?
(BTW, mijn interesse komt helemaal niet voort uit de wens om de performance te optimaliseren; ik wil gewoon weten welke vormen er voor mij als ontwikkelaar beschikbaar zijn).
Voorbeeld van elke soort die in de vraag wordt genoemd:
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());
}
}
Ik weet niet wat u als pathologisch beschouwt, maar laat mij u enkele alternatieven geven die u misschien nog niet gezien hebt:
List<E> sl= list ;
while( ! sl.empty() ) {
E element= sl.get(0) ;
.....
sl= sl.subList(1,sl.size());
}
Of zijn recursieve versie:
void visit(List<E> list) {
if( list.isEmpty() ) return;
E element= list.get(0) ;
....
visit(list.subList(1,list.size()));
}
Ook een recursieve versie van het klassieke for(int=0...
:
void visit(List<E> list,int pos) {
if( pos >= list.size() ) return;
E element= list.get(pos) ;
....
visit(list,pos+1);
}
Ik noem ze omdat je "enigszins nieuw bent in Java" en dit interessant kan zijn.
Je zou altijd het eerste en derde voorbeeld kunnen vervangen door een while-lus en wat meer code. Dit geeft je het voordeel dat je de do-while kunt gebruiken:
int i = 0;
do{
E element = list.get(i);
i++;
}
while (i < list.size());
Natuurlijk kan dit soort dingen een NullPointerException veroorzaken als list.size() 0 teruggeeft, omdat het altijd minstens één keer wordt uitgevoerd. Dit kan worden opgelost door te testen of het element null is voordat je zijn attributen / methoden gebruikt. Toch is het veel eenvoudiger en makkelijker om de for-lus te gebruiken