Co zostanie wyświetlone na konsoli przez poniższy kod? Dla utrudnienia nie podaję możliwych odpowiedzi.
public class ListTest { public static void main(String[] args) { List<String> planets = new ArrayList<String>(); planets.add("Mercury"); planets.add("Venus"); planets.add("Earth"); planets.add("Mars"); for (String p : planets) { if (p.startsWith("V")) { print(planets); } } } private static void print(final List<String> planets) { Collections.sort(planets); for (String p : planets) { System.out.print(p + " "); } System.out.println(""); } }
Aby zobaczyć odpowiedź, zaznacz tekst w poniższym bloku:
Dlaczego tak się dzieje? Problem leży w dwóch miejscach.
Standardowo kolekcje (w tym ArrayList) zwierają pole modCount. Jest ono inkrementowane, za każdym razem, gdy modyfikujemy naszą kolekcję. Ten kod: for (String p : planets) zostanie natomiast zamieniony na coś takiego:
Iterator<String> iterator = planets.iterator(); while (iterator.hasNext()) { String p = iterator.next();
Czyli zostanie utworzony Iterator, po którym będziemy iterować, aż do końca Listy. Podczas tworzenia takiego Iteratora, zapamiętywany jest aktualny licznik modyfikacji w iteratorze, który później, podczas wywołania metody next(), jest porównywany z obecną wartością. Gdybyśmy zamiast sort() wywoływali add() lub remove() to poleciałby ConcurrentModificationException. Niestety metoda sort() nie powoduje modyfikacji tego licznika! Nie pomaga nawet Collections.synchronizedList()!
Jest to "piękny" przykład efektów ubocznych, które mogą się zdarzyć w naszym kodzie, a których znalezienie może zająć sporo czasu. A może czas na niemodyfikowalne kolekcje w Javie tak jak w Scali?
Brak komentarzy:
Prześlij komentarz