Здравствуйте кидает Exception ConcurrentModificationException на этой строчке for (Transaction t : c.getTransactions())(64 строчка) на 2 итерации. Саму суть ошибки понимаю - случается она тогда, когда в цикле происходит удаление элементов итератора. Пытался remove изменить на removeIF, но ошибка та же.Это уникальный вопрос, потому что он возникает, когда все требования по его решению соблюдены.
private static void iteration() {
Integer n = 1;
Integer k = 1;
Double maxProfit;
Integer clusterInd;
while (k > 0) {
System.out.println("Итерация " + n);
n++;
k = 0;
for (int i=0;i<clusters.size();i++) {
Cluster c = clusters.get(i);
for (Transaction t : c.getTransactions()) {//ошибку кидает здесь на 2 итерации
maxProfit = profit();
clusterInd = -1;
c.deleteTransaction(t);
int j = 0;
for (Cluster cl : clusters) {
if (j != i) {
cl.addTransaction(t);
Double p = profit();
if (p > maxProfit) {
maxProfit = p;
clusterInd = j;
}
cl.deleteTransaction(t);
}
j++;
}
if (clusterInd == -1){
clusters.get(i).addTransaction(t);
}else {
k++;
clusters.get(clusterInd).addTransaction(t);
}
}
}
}
System.out.println(k);
}
Функция, в которой происходит удаление транзакции:
public void deleteTransaction(Transaction m) {
if (this.count > 0) {
String[] trans = m.getTrans();
for (String s : trans) {
this.square--;
if (freq.containsKey(s)) {
if (freq.get(s) > 0) {
this.freq.put(s, freq.get(s) - 1);
if (this.freq.get(s) == 0) {
this.width--;
freq.entrySet().removeIf(entry -> entry.getKey().equals(s));
}
}
}
}
this.count--;
if (this.count > 0) {
this.height = (double) this.square / this.width;
} else {
this.height = 0.0;
}
transactions.removeIf(m::equals);
}
}
Логи:
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:939)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:893)
at com.lab6.ClopeAlgorithm.iteration(ClopeAlgorithm.java:64)
at com.lab6.ClopeAlgorithm.main(ClopeAlgorithm.java:96)
Создадим небольшую мапу:
final HashMap<Object, Object> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
Следующие выражения выбьют ошибку:
Java 8+:
map.entrySet().stream().filter((i) -> ("3".equals(i.getKey()))).forEachOrdered((i) -> {
map.remove(i.getKey());
});
Java < 8:
for (Entry<Object, Object> i : map.entrySet()) {
if ("2".equals(i.getKey())) {
map.remove(i.getKey());
}
}
Выражения, которые не выдадут ошибок:
Работает только на 1 удаление, если добавить еще один if для удаления, выдаст ошибку NoSuchElementException
final Iterator<Entry<Object, Object>> i = map.entrySet().iterator();
while (i.hasNext()) {
if ("2".equals(i.next().getKey())) {
i.remove();
}
}
Если хотим удалить два и более элемента, можно использовать:
while (i.hasNext()) {
switch ((String) i.next().getKey()) {
case"2":
case"3":
i.remove();
break;
}
}
Для удаления, добавления, изменения, лучше использовать switch:
final Map<Object, Object> map = new ConcurrentHashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
map.put("4", "5");
final Iterator<Entry<Object, Object>> i = map.entrySet().iterator();
while (i.hasNext()) {
switch ((String) i.next().getKey()) {
case "3":
i.remove();
map.put("5", "5");
final Iterator<Entry<Object, Object>> it = map.entrySet().iterator();
while (it.hasNext()) {
if ("2".equals((String) it.next().getKey())) {
it.remove();
map.put("5", "5");
map.put("6", "6");
map.remove(map.get("1"));
}
}
break;
}
}
System.out.println(map); // {4=5, 5=5, 6=6}
Но опять-же у этого всего есть побочные эффекты.
Если в хранилище залетел какой-то объект, во время:
while (i.hasNext()) {
switch((String)i.next().getKey()) {
//.....
}
}
Получим ConcurrentModificationException.
В вашем случае можно использовать подобно mutex.
synchronized(map) {
//....
}
Тогда можно будет не бояться, что залетает.
Вместо цикла foreach используйте явный итератор и его метод remove.
Подсказали решение:
Изменить for (Transaction t : c.getTransactions()) на
for (int i=0;i<clusters.size();i++) {
Cluster c = clusters.get(i);
List<Transaction> transactions = new ArrayList<>(c.getTransactions());
for (Transaction t : transactions) {
...
}
}
Foreach – это механизм для работы с элементами коллекции, а не с самой коллекцией. Использование foreach для модификации коллекции – это уже не правильно. А писать костыли и велосипеды, чтобы «расширить возможности» конструкции языка – и вовсе плохая затея. Этот подход сломает возможность заменить реализацию А на реализацию Б без танцев с бубном и переписывания всего кода, где коллекция модифицировалась в foreach.
Вот один из классических подходов работы с итераторами:
for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
if (it.next() % 2 == 0) {
it.remove();
}
}
Взято отсюда:
https://habr.com/post/325426/#comment_10149968
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости