Fiind oarecum nou pentru limbajul Java-am'm încercat să mă familiarizez cu toate modurile (sau cel puțin non-patologice) care s-ar putea repeta printr-o lista (sau poate alte colecții) și avantajele sau dezavantajele fiecăreia.
Dat-o
în timp ce
/ a face în timp ce
bucle la fel de bine)// 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
// ...
}
Notă: Ca @amarseillan subliniat, această formă este o alegere proastă
pentru iterarea peste Lista lui, pentru punerea în aplicare efectivă a
a "obține" metodă nu poate fi la fel de eficient ca atunci când se utilizează un Iterator. De exemplu,
LinkedList` implementări trebuie să traverseze toate
elementele precedente am să te i-lea element.
În exemplul de mai sus nu's nici un fel de "Listă" punerea în aplicare a "salva locul său" pentru a face iterații viitoare mai eficiente. Pentru un ArrayList` nu't contează cu adevărat, deoarece complexitatea/costul de "a lua" este constanta de timp (O(1)) întrucât pentru LinkedList este proporțională cu mărimea de lista (O(n)).
Pentru mai multe informații despre complexitatea de calcul a construit-în "Colecții" de implementări, a verifica afară acest question.
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
(O hartă metoda de Java 8's Stream API (a se vedea @i_am_zero's de răspuns).)
În Java 8 colecție de clase care implementeaz Iterable
(de exemplu, toate `Lista lui) au acum o "forEach" metoda, care poate fi folosit în loc de [buclă declarație][for-loop] s-a demonstrat mai sus. (Aici este [o altă întrebare][java-8-foreach-comparație], care oferă o comparație bună.)
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).
Ce alte metode sunt acolo, dacă orice?
(BTW, interesul meu nu provine deloc din dorința de a [a optimiza performanța][iterator-performanță-întrebare]; vreau doar să știu ce forme sunt disponibile pentru mine ca un dezvoltator.)
Cele trei forme de looping sunt aproape identice. Îmbunătățită "pentru" bucla:
for (E element : list) {
. . .
}
este, conform Java Language Specification, identical în vigoare la utilizarea explicită a unui iterator cu un tradițional "pentru" bucla. În al treilea caz, puteți modifica doar lista de conținuturi prin eliminarea elementul curent, și atunci numai dacă o faci prin "eliminați" metoda iterator în sine. Cu index pe baza de repetare, ești liber să modifice lista în orice mod. Cu toate acestea, adăugarea sau eliminarea de elemente care înainte de a veni indicele curent riscuri având în buclă sărind peste elemente sau prelucrare același element de mai multe ori; aveți nevoie pentru a ajusta bucla indice în mod corespunzător atunci când face astfel de modificări.
În toate cazurile, element
este o referință la real element de listă. Nici una din metode de repetare face o copie de nimic în listă. Modificări în starea internă a element
va fi întotdeauna văzut în starea internă de elementul corespunzător de pe lista.
În esență, există doar două moduri de a repeta peste o listă: prin utilizarea unui index sau folosind un iterator. Îmbunătățită pentru bucla este doar un sintactică de comenzi rapide introdus în Java 5, pentru a evita plictiseala de explicit defini un iterator. Pentru ambele stiluri, puteți veni cu, în esență, banal variante, folosind "pentru", în timp ce " sau " a face în timp ce` blocuri, dar toate se reduc la același lucru (sau, mai degrabă, două lucruri).
EDIT: Ca @iX3 puncte într-un comentariu, puteți utiliza un ListIteratorpentru a seta elementul curent din lista ca esti iterarea. Tu ar trebui să utilizați [
Lista#listIterator()][2] în loc de [
Lista#iterator()][3] pentru a inițializa variabila buclă (care, evident, ar trebui să fie declarat un ListIterator
, mai degrabă decât un Iterator`).
Exemplu din fiecare tip enumerate în întrebare:
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());
}
}
În Java 8 avem mai multe moduri de a repeta peste colecție de clase.
Colecțiile care pune în aplicare Iterable
(de exemplu toate listele) au acum "forEach" metoda. Putem folosi metoda de referință a introdus în Java 8.
Arrays.asList(1,2,3,4).forEach(System.out::println);
De asemenea, putem repeta peste o listă utilizând Stream ca:
Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
Arrays.asList(1,2,3,4).stream().forEachOrdered(System.out::println);
Ar trebui să prefera forEachOrdered
peste "forEach" deoarece comportamentul de "forEach" este în mod explicit nondeterminist unde ca forEachOrdered
efectuează o acțiune pentru fiecare element din acest flux, în întâlnirea ordinea de flux în cazul în care fluxul are o definite întâlni ordine. Deci forEach nu garantează că ordinul ar fi păstrat.
Avantajul cu fluxuri este că putem face, de asemenea, utilizarea de fluxuri paralele acolo unde este cazul. Dacă obiectivul este de doar pentru a imprima elemente, indiferent de ordinea atunci putem folosi flux paralel ca:
Arrays.asList(1,2,3,4).parallelStream().forEach(System.out::println);
Eu nu't știu ce consideră patologice, dar lasă-mă să prevadă unele alternative nu ai văzut înainte:
List<E> sl= list ;
while( ! sl.empty() ) {
E element= sl.get(0) ;
.....
sl= sl.subList(1,sl.size());
}
Sau recursive versiune:
void visit(List<E> list) {
if( list.isEmpty() ) return;
E element= list.get(0) ;
....
visit(list.subList(1,list.size()));
}
De asemenea, un recursiv versiune a clasic pentru(int i=0...
:
void visit(List<E> list,int pos) {
if( pos >= list.size() ) return;
E element= list.get(pos) ;
....
visit(list,pos+1);
}
Le-am spus pentru că sunt "oarecum nou pentru Java" și acest lucru ar putea fi interesant.
Puteți utiliza forEach. începând cu Java 8:
List<String> nameList = new ArrayList<>(
Arrays.asList("USA", "USSR", "UK"));
nameList.forEach((v) -> System.out.println(v));
În java 8puteți utiliza Lista.forEach()
metoda cu expresie lambda
de a repeta peste o listă.
import java.util.ArrayList;
import java.util.List;
public class TestA {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("Apple");
list.add("Orange");
list.add("Banana");
list.forEach(
(name) -> {
System.out.println(name);
}
);
}
}
Chiar, de multe alternative sunt listate. Cel mai simplu și mai curat ar fi utilizarea sporită "pentru" declarația de mai jos. La "Expresie" este de un anumit tip, care este iterable.
for ( FormalParameter : Expression ) Statement
De exemplu, pentru a itera prin, Lista<String> id-uri, putem pur și simplu așa,
for (String str : ids) {
// Do something
}
Pentru o înapoi de căutare ar trebui să utilizați următoarele:
for (ListIterator<SomeClass> iterator = list.listIterator(list.size()); iterator.hasPrevious();) {
SomeClass item = iterator.previous();
...
item.remove(); // For instance.
}
Dacă vrei să știi o poziție, utilizați iterator.previousIndex(). Aceasta ajută, de asemenea, să scrie o buclă internă care compară două poziții în listă (iteratori nu sunt egale).
Ai putea să comutați întotdeauna prima și a treia exemple cu o buclă de timp și un pic mai mult cod. Acest lucru vă oferă avantajul de a fi capabil de a utiliza în timp ce do -:
int i = 0;
do{
E element = list.get(i);
i++;
}
while (i < list.size());
Desigur, acest tip de lucru ar putea duce la o NullPointerException daca lista.size() returneaza 0, pentru că întotdeauna este executat cel puțin o dată. Acest lucru poate fi stabilit prin testarea dacă elementul este nul înainte de a utiliza atributele sale / metode tho. Totuși, l's mult mai simplu și mai ușor de utilizat pentru bucla