Java Language
Iterator i Iterable
Szukaj…
Wprowadzenie
java.util.Iterator
to standardowy interfejs Java SE dla obiektów implementujących wzorzec projektowy Iterator. Interfejs java.lang.Iterable
jest przeznaczony dla obiektów, które mogą zapewnić iterator.
Uwagi
Możliwe jest iterowanie po tablicy za pomocą pętli for
-each, chociaż tablice java nie implementują Iterable; iteracja jest wykonywana przez JVM przy użyciu niedostępnego indeksu w tle.
Korzystanie z pętli Iterable in for
Ćwiczenia wdrażające Iterable<>
interfejs może być używany w for
pętli. W rzeczywistości jest to tylko cukier syntaktyczny do pobierania iteratora z obiektu i używania go do sekwencyjnego pobierania wszystkich elementów; sprawia, że kod jest bardziej przejrzysty, szybszy do pisania i mniej podatny na błędy.
public class UsingIterable {
public static void main(String[] args) {
List<Integer> intList = Arrays.asList(1,2,3,4,5,6,7);
// List extends Collection, Collection extends Iterable
Iterable<Integer> iterable = intList;
// foreach-like loop
for (Integer i: iterable) {
System.out.println(i);
}
// pre java 5 way of iterating loops
for(Iterator<Integer> i = iterable.iterator(); i.hasNext(); ) {
Integer item = i.next();
System.out.println(item);
}
}
}
Korzystanie z surowego iteratora
Podczas gdy korzystanie z pętli foreach (lub „Extended for loop”) jest proste, czasem korzystne jest bezpośrednie użycie iteratora. Na przykład, jeśli chcesz wydzielić kilka wartości oddzielonych przecinkami, ale nie chcesz, aby ostatni element zawierał przecinek:
List<String> yourData = //...
Iterator<String> iterator = yourData.iterator();
while (iterator.hasNext()){
// next() "moves" the iterator to the next entry and returns it's value.
String entry = iterator.next();
System.out.print(entry);
if (iterator.hasNext()){
// If the iterator has another element after the current one:
System.out.print(",");
}
}
Jest to o wiele łatwiejsze i bardziej zrozumiałe niż posiadanie zmiennej isLastEntry
lub wykonywanie obliczeń za pomocą indeksu pętli.
Tworzenie własnej iterowalności.
Aby stworzyć własną Iterable jak w każdym interfejsie, wystarczy zaimplementować w interfejsie metody abstrakcyjne. W przypadku Iterable
istnieje tylko jeden, który nazywa się iterator()
. Jednak jego zwracany typ Iterator
jest interfejsem z trzema metodami abstrakcyjnymi. Możesz zwrócić iterator powiązany z niektórymi kolekcjami lub utworzyć własną niestandardową implementację:
public static class Alphabet implements Iterable<Character> {
@Override
public Iterator<Character> iterator() {
return new Iterator<Character>() {
char letter = 'a';
@Override
public boolean hasNext() {
return letter <= 'z';
}
@Override
public Character next() {
return letter++;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Doesn't make sense to remove a letter");
}
};
}
}
Używać:
public static void main(String[] args) {
for(char c : new Alphabet()) {
System.out.println("c = " + c);
}
}
Nowy Iterator
powinien mieć stan wskazujący na pierwszy element, każde wywołanie next aktualizuje swój stan, wskazując na następny. hasNext()
sprawdza, czy iterator jest na końcu. Gdyby iterator był połączony z kolekcją, którą można modyfikować, opcjonalna metoda remove()
iteratora mogłaby zostać zaimplementowana w celu usunięcia elementu wskazanego obecnie z kolekcji podstawowej.
Usuwanie elementów za pomocą iteratora
Metoda Iterator.remove()
jest opcjonalną metodą, która usuwa element zwrócony przez poprzednie wywołanie Iterator.next()
. Na przykład poniższy kod zapełnia listę ciągów, a następnie usuwa wszystkie puste ciągi.
List<String> names = new ArrayList<>();
names.add("name 1");
names.add("name 2");
names.add("");
names.add("name 3");
names.add("");
System.out.println("Old Size : " + names.size());
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String el = it.next();
if (el.equals("")) {
it.remove();
}
}
System.out.println("New Size : " + names.size());
Wynik :
Old Size : 5
New Size : 3
Zauważ, że powyższy kod to bezpieczny sposób usuwania elementów podczas iteracji typowej kolekcji. Jeśli zamiast tego spróbujesz usunąć elementy z takiej kolekcji:
for (String el: names) {
if (el.equals("")) {
names.remove(el); // WRONG!
}
}
typowa kolekcja (taka jak ArrayList
), która udostępnia iteratorom semantykę szybkiego iteratora, zgłosi wyjątek ConcurrentModificationException
.
Metoda remove()
może być wywołana (tylko raz) po next()
wywołaniu next()
. Jeśli zostanie wywołane przed wywołaniem next()
lub jeśli zostanie wywołane dwukrotnie po wywołaniu next()
, wówczas wywołanie remove()
spowoduje zgłoszenie IllegalStateException
.
Operacja remove
jest opisana jako operacja opcjonalna ; tzn. nie wszystkie iteratory na to pozwolą. Przykłady, w których nie jest obsługiwane, to iteratory dla niezmiennych kolekcji, widoki kolekcji tylko do odczytu lub kolekcje o stałym rozmiarze. Jeśli funkcja remove()
zostanie wywołana, gdy iterator nie obsługuje usuwania, zgłosi UnsupportedOperationException
.