Będąc nieco nowym językiem Java, próbuję zapoznać się ze wszystkimi sposobami (a przynajmniej niepatologicznymi), które można iterować po liście (lub innych kolekcjach) oraz zaletami i wadami każdego z nich.
Biorąc pod uwagę List<E> list
obiekt, znam następujące sposoby przechodzenia przez wszystkie elementy:
Podstawowy dla pętli (oczywiście istnieją również odpowiedniki while
/ do while
pętle)
// 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
// ...
}
Uwaga: Jak zauważył @amarseillan, ta forma jest kiepskim wyborem dla iteracji w porównaniu z List
s, ponieważ faktyczna implementacja get
metody może nie być tak wydajna jak w przypadku korzystania z Iterator
. Na przykład LinkedList
implementacje muszą przechodzić przez wszystkie elementy poprzedzające i, aby uzyskać i-ty element.
W powyższym przykładzie List
implementacja nie może „zapisać swojego miejsca”, aby zwiększyć wydajność przyszłych iteracji. Dla a ArrayList
tak naprawdę nie ma to znaczenia, ponieważ złożoność / koszt get
to stały czas (O (1)), podczas gdy dla a LinkedList
jest proporcjonalny do wielkości listy (O (n)).
Aby uzyskać więcej informacji na temat złożoności obliczeniowej wbudowanych Collections
implementacji, sprawdź to pytanie .
Ulepszone dla pętli (ładnie wyjaśnione w tym pytaniu )
for (E element : list) {
// 1 - can call methods of element
// ...
}
Iterator
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
// ...
}
Funkcjonalna Java
list.stream().map(e -> e + 1); // Can apply a transformation function for e
Iterable.forEach , Stream.forEach ...
(Metoda mapy z Stream API Java 8 (patrz odpowiedź @ i_am_zero).)
W klasach Java 8, które implementują Iterable
(na przykład wszystkie List
s), istnieje teraz forEach
metoda, której można użyć zamiast instrukcji pętli for pokazanej powyżej. (Oto kolejne pytanie, które zapewnia dobre porównanie).
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).
Jakie są inne sposoby, jeśli istnieją?
(BTW, moje zainteresowanie wcale nie wynika z chęci optymalizacji wydajności ; chcę tylko wiedzieć, jakie formy są dostępne jako programista).
List
interfejsu jest specyficzne?