Я пытаюсь понять, есть ли возможность для чего-то (не говорите мне, что это не так, это мой неудачный проект) по расположению точек и их расстояний.

for (Point p1 : results) {
    remove.clear();
    for (Point p2 : results) {
        if (Math.sqrt(
            Math.pow(p1.getX() - p2.getX(), 2)
            + Math.pow(p1.getY() - p2.getY(), 2)
        ) % 1 > 0) {
            results.remove(p2);
        }
    }
}

По сути, я пытаюсь проверить, имеют ли две точки целочисленное расстояние, и, если нет, удалить его из набора и сделать это для всех точек (которые остались).

Однако я получаю ConcurrentModificationException и не знаю, как его реорганизовать для выполнения той же задачи, не вызывая ошибки другим способом.

Есть ли способы обойти это или это просто ограничение Java?

РЕДАКТИРОВАТЬ: хотя дублирующая предложенная ссылка предлагает понимание, сосредоточение ответов на одиночных петлях имеет место излишка, что не применимо. Если этот вопрос повторяется, это означает, что использование Iterator - это только ответ.

0
Captain Prinny 7 Июл 2015 в 20:12
2
На самом деле это не дубликат, поскольку он включает вложенный цикл, чего нет в указанном вопросе.
 – 
Warren Dew
7 Июл 2015 в 20:16
@ Уоррен: Я не понимаю, как дополнительный цикл меняет проблему или решение. Как вы думаете, будет ли другой способ решения проблемы из-за лишнего цикла?
 – 
sstan
7 Июл 2015 в 20:20
1
Это не работает напрямую, для этого потребуется дополнительная обработка, потому что после удаления точки она становится недействительной для всех будущих обсуждений. Он дает основу для решения, но сам по себе не является таковым.
 – 
Captain Prinny
7 Июл 2015 в 20:21
Связанное решение может работать для внутреннего цикла, но внешний цикл все равно будет генерировать исключение ConcurrentModificationException, даже если вы используете Iterator. Чтобы использовать Iterator во внутреннем цикле, автору нужно будет независимо отслеживать, какие элементы были проверены во внешнем цикле, а не использовать Iterator или конструкцию for. Это может означать, что было бы лучше не использовать итератор, а вместо этого отдельно отслеживать, какие элементы удалять, и удалять их в конце.
 – 
Warren Dew
7 Июл 2015 в 20:22

4 ответа

Лучший ответ

Некоторые реализации Collection используют итератор « безотказно ». При удалении элемента из Collection напрямую (с использованием Collection#remove) во время итерации по нему будет возникать исключение.

Расширенные циклы for используют итератор коллекции для итерации по коллекции.

Вы можете изменить свои расширенные циклы на обычные циклы for:

for(int i = 0; i < results.size(); i++) {
    for(int j = 0; j < results.size(); j++) {
    Point result = results.get(j);
        if(...) {
            //results.remove(j); or
            //results.remove(result);
        }
    }
}

Как упоминалось в комментариях, это не сработает для Set. В этом случае вы можете просто сохранить ссылку на итератор коллекции и использовать ее для удаления элемента:

Iterator<Point> firstIter = results.iterator();
while(firstIter.hasNext()) {
    Point p1 = iterator.next();

    Iterator<Point> secondIter = results.iterator();
    while(secondIter.hasNext()) {
        Point p2 = secondIter.next();

        if(...) {
            secondIter.remove();
        }
    }
}
1
Dioxin 7 Июл 2015 в 21:02
Это работает, только если results является List, но не Set или другим типом Collection.
 – 
Sam Estep
7 Июл 2015 в 20:27
Проверьте заголовок .. " Нужна помощь в изменении списка "
 – 
Dioxin
7 Июл 2015 в 20:29
Нигде в заголовке или посте не появляется «Список» с большой буквы. Слово "набор" ( "удалить его из набора " ), однако, появляется, что дает мне основания полагать, что этот вопрос касается произвольных коллекций.
 – 
Sam Estep
7 Июл 2015 в 20:30
Это то, к чему я пошел сразу, пока ждал. Изначально я использовал List, но я думаю о нем как о «наборе» вещей, с моей плохой строгостью.
 – 
Captain Prinny
7 Июл 2015 в 20:42
Редактирую свой ответ
 – 
Dioxin
7 Июл 2015 в 20:45

Вы могли сделать это:

Iterator<Point> iterator = results.iterator();
while (iterator.hasNext()) {
  Point p1 = iterator.next();
  boolean shouldBeRemoved = false;
  for(Point p2 : results) {
    if (p2 != p1 && (Math.sqrt(Math.pow(p1.getX() - p2.getX(), 2)
                             + Math.pow(p1.getY() - p2.getY(), 2))
                     % 1 > 0)) {
      shouldBeRemoved = true;
      break;
    }
  }
  if (shouldBeRemoved) {
    iterator.remove();
  }
}

Разница в том, что, очевидно, удаляется p1 вместо p2, но поскольку мы имеем дело с Set здесь ...

удалить его из набора

... порядок не важен, правда?

1
Sam Estep 7 Июл 2015 в 20:22
Хорошая основа, но она все равно должна пройти через весь набор p2. Кажется, что проблема заключается в том, чтобы делать это с помощью итераций.
 – 
Captain Prinny
7 Июл 2015 в 20:24
Под "проблемой", я полагаю, вы имеете в виду ConcurrentModificationException. Действительно ли опубликованный мной код вызывает такое исключение?
 – 
Sam Estep
7 Июл 2015 в 20:25
Думаю, может и нет, но пока ждал, я пробовал свои собственные модификации. В настоящее время жду завершения или неудачи.
 – 
Captain Prinny
7 Июл 2015 в 20:26
Не все реализации Set неупорядочены. И набор не пишется с заглавной буквы.
 – 
Dioxin
7 Июл 2015 в 20:56

Похоже, это происходит из-за того, что вы пытаетесь удалить ту же структуру Point. Рассмотрим случай с первым пунктом. И p1, и p2 относятся к первой точке результатов. Расстояние между p1 и p2 равно нулю, потому что они относятся к одной и той же точке. Затем вы пытаетесь удалить p2, который на самом деле является p1. См. Ссылку http://docs.oracle. com / javase / 7 / docs / api / java / util / ConcurrentModificationException.html, чтобы узнать больше о том, почему вы можете получить это исключение даже в случае, когда один поток пытается получить доступ и изменить некоторую структуру.

Вы можете изменить приведенный выше код следующим образом: -

boolean[] if_deleted = new boolean[results.size()];

for (int i = 0; i < results.size(); ++i) {
    if_deleted[i] = false;
}

for (int i = 0; i < results.size(); ++i){
    for(int j = i + 1; j < results.size(); ++j)
            Point p1 = (Point)results.get(i);
            Point p2 = (Point)results.get(j);
            if (!if_deleted[i] && !if_deleted[j]) { 
                if (Math.sqrt(
                    Math.pow(p1.getX() - p2.getX(), 2)
                            +
                            Math.pow(p1.getY() - p2.getY(), 2))
                    % 1 > 0){
                        if_deleted[j] = true;
                        results.remove(p2);
                }
            }    
    }
}

for (int i = 0; i < results.size(); ++i) {
    if (if_deleted[i]) {
        results.remove(i);
    }
}
0
gaurav gupta 7 Июл 2015 в 20:25
Это работает, только если results является List, но не Set или другим типом Collection.
 – 
Sam Estep
7 Июл 2015 в 20:27
Неважно, есть это или нет, пробовать. Предоставление «объявление переменной здесь запрещено», поэтому требуется дополнительный рефакторинг. Отсутствует {после секунды для.
 – 
Captain Prinny
7 Июл 2015 в 20:31
Я пытаюсь предложить способ, с помощью которого может сработать предполагаемое поведение. Это не полное и компилируемое решение как таковое.
 – 
gaurav gupta
7 Июл 2015 в 20:39
Ах, моя беда. На самом деле, похоже, это не работает. Я не смотрел на это сильно, но похоже, что он где-то застрял в бесконечном цикле. Математическая логика фактически не удаляет точку, поскольку в моем работающем коде она отлично выводит 0,0 (точка «1»).
 – 
Captain Prinny
7 Июл 2015 в 20:44

Я рефакторинг

    for (int p1 = 0;  p1 < results.size() ; p1++){
        for (int p2 = p1;  p2 < results.size() ; p2++){
                if (Math.sqrt(
                        Math.pow(results.get(p1).getX() - results.get(p2).getX(), 2)
                            +
                        Math.pow(results.get(p1).getY() - results.get(p2).getY(), 2))
                        % 1 > 0){
                            results.remove(p2);
                            p2--;
                }
        }
    }

Но я не уверен, что он работает так, как я ожидал.

0
Captain Prinny 7 Июл 2015 в 20:31