czwartek, 7 listopada 2013

Błędne użycie Listy w Javie

Ostatnio natrafiłem na ciekawą "zagadkę" związaną z kolekcjami w Javie. Czy jest to coś nowego, czy już gdzieś było w Java Puzzlers - nie wiem. Prezentowałem ten kod kolegom z zespołu i oni nie wpadli na właściwe rozwiązanie, więc postanowiłem je tutaj opublikować.

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:

Earth Mars Mercury Venus
Earth Mars Mercury Venus 

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