Modyfikacja kolekcji w czasie jej iteracji

0

Witam mam taka funkcje. Wyszukuje ona pola na ktore moze stanac gracz. Potrzebuje do tego jego obecnej pozycji (w postaci pola ktore zajmuje) i ilosci krokow ktore moze wykonac.

    //Zwraca mozliwe pola w postaci punktow ich srodkow
    private Point2D[] getAvailableFields(Field startField, int maxDistance) {
        Point2D start = startField.getCenter();
        int sx = (int) start.getX();
        int sy = (int) start.getY();
        List<TestPoint> points = new LinkedList<>();
        List<Point2D> correctPoints = new LinkedList<>();
        //Dodanie 4 punktow na start
        points.add(new TestPoint(sx, sy - GP.FIELD_SIZE, 1));
        points.add(new TestPoint(sx + GP.FIELD_SIZE, sy, 1));
        points.add(new TestPoint(sx, sy + GP.FIELD_SIZE, 1));
        points.add(new TestPoint(sx - GP.FIELD_SIZE, sy, 1));
        //Znalezienie wszystkich mozliwych pol
        ListIterator<TestPoint> iterator = points.listIterator();
        while (iterator.hasNext()) {
            TestPoint point = iterator.next();
            Field field = getFieldFromPoint(point);
            //Jesli punkt jest poza granica gry, nie jest stepable
            //lub przekoczyl dystans to go usuwamy
            if (field == null || !field.isStepable()
                    || point.getDistance() == maxDistance) {
                iterator.remove();
                continue;
            }
            //Jezeli nie to punkt jest wlasciwy
            correctPoints.add(new Point2D.Double(point.getX(), point.getY()));
            //Dalej..
            if (field.isVisited()) { //Jesli pole bylo juz odwiedzone to usuwamy
                iterator.remove();
                continue;
            } else { //A jak nie to dodajemy 4 nowe punkty
                int px = (int)point.getX();
                int py = (int)point.getY();
                int dist = point.getDistance() + 1;
                iterator.add(new TestPoint(px, py - GP.FIELD_SIZE / 2, dist));
                iterator.add(new TestPoint(px + GP.FIELD_SIZE / 2, py, dist));
                iterator.add(new TestPoint(px, py + GP.FIELD_SIZE / 2, dist));
                iterator.add(new TestPoint(px - GP.FIELD_SIZE / 2, py, dist));
            }
        }
        //Zwrocenie wyniku
        return correctPoints.toArray(new Point2D.Double[correctPoints.size()]);
    }

Kod nie wyrzuca zadnego wyjatku ale nie dziala poprawnie. Wyglada to troche jakby te cztery punkty (w petli) byly dodawane ale nie byly potem iterowane). Tak wiec mam dwa pytania:

  1. Jezeli zastapie instrukcje continue instrukcjami *iterator.next() *to dostane blad (null exception). Czemu tak sie dzieje? Czym sie rozni wywolanie continue od iterator.next()?
  2. Czy z iterator.add() wszystko robie dobrze? Czy moze powinienem po kazdym dodaniu wywolac iterator.next()? Jak tak probowalem to dostawalem NotSuchElementException. Co zrobic zeby dodac te 4 punkty na koncu listy i iterowac dalej? Moze uzycie listy umozliwiajacej random access ułatwiło by sprawe?
0

iterator.remove() powtarzane w pętli, to raczej kiepskie wyjście. Przeiteruj się przez kolekcję zapisując do oddzielnej tablicy (np. "toRemove") obiekty, które mają został wywalone z pierwotnej tablicy, a następnie w pętli iteruj przez toRemove i usuwaj pobrane stamtąd elementy z bazowej tablicy.
W twoim przypadku (tak mniej więcej):

HashSet<TestPoint> toRemove = new HashSet<>();
ListIterator<TestPoint> iterator = points.listIterator();
while (iterator.hasNext()) {
TestPoint point = iterator.next();
....
   if (field.isVisited()) { //Jesli pole bylo juz odwiedzone to usuwamy
           toRemove.add(point);
   }
....
}
for (TestPoint pointToRemove : toRemove){
   points.remove(pointToRemove);
}

Jak nie zadziała, to pomyśl o zastąpieniu list LinkedList zbiorem, np. HashSet() - powinno przy okazji działać trochę szybciej.

0

Dlaczego iterator.remove() to kiepskie wyjscie? Zdecydowanie bardziej wydajne niz to co zaproponowano powyzej - iterator wie jak optymalnie usunac dany element z listy.
Co do problemu - uzywasz ListIterator.add, ktorej dokumentacja mowi miedzy innymi:

(...)The new element is inserted before the implicit cursor: a subsequent call to next would be unaffected(...)

Co do pytania to nie wiem co chcesz dokladnie zrobic, ale ListIterator me metode previous() ktora zwroci ten nowo dodany element (co zreszta jest opisane zaraz po fragmencie ktory przytoczylem powyzej). Aby przetworzyc te 4 nowe punkty musialbys zatem odpowiednio wywolywac metode previous() (pewnie dla kazdego dodania, w odpowiedniej kolejnosci). Aby uniknac powtornego powtorzenia punktu ktory spowodowal dodanie tych 4 nowych musialbys go w sumie usunac zaraz przed dodaniem. Cos w tym stylu:

class Point {
    public final int x, y;
    Point(int x, int y) {
        this.x = x;this.y = y;
    }
    @Override
    public String toString() {
        return String.format("Point[x=%d, y=%d]", x, y);
    }
}

// ...

List<Point> points = new LinkedList<Point>();
int pointsCount = 3;
for (int i = 0; i < pointsCount; ++i) {
    points.add(new Point(i + 1, pointsCount - i));
}

ListIterator<Point> iter = points.listIterator();
while (iter.hasNext()) {
    Point p = iter.next();
    if (p.x != p.y) {
        System.out.println(p);
    } else {
        iter.remove();
        iter.add(new Point(101, 102));
        iter.add(new Point(202, 203));
        iter.add(new Point(303, 304));
        iter.add(new Point(404, 405));
        iter.previous();
        iter.previous();
        iter.previous();
        iter.previous();
    }
}

co wypisze:
Point[x=1, y=3]
Point[x=101, y=102]
Point[x=202, y=203]
Point[x=303, y=304]
Point[x=404, y=405]
Point[x=3, y=1]

Czyli jak widac: przetorzy 1 i ostatni punkt bez zmian, a zamiast 2 (x == y) wstawi 4 inne punkty i je rowniez przetworzy w tej samej petli.

0

Mućka ma rację. Moje rozwiązanie jest bezpieczniejsze (wątki, wiele iteratorów do tej samej kolekcji i inne cuda), ale Tobie wystarczy remove().

1 użytkowników online, w tym zalogowanych: 0, gości: 1