2009-05-26 2 views
5

Я выполняю итерацию над JRE Collection, которая обеспечивает безупречную концепцию итератора. Проблема в том, что мне нужно удалить логический партнер объекта, если объект соответствует условию. Таким образом, предотвращается также переработка партнера. Может ли кто-то предложить лучший тип сбора для этой цели?Collection Alternative - ConcurrentModificationException

Пример.

myCollection<BusinessObject> 

for (BusinessObject anObject : myCollection) 
{ 
    if (someConditionIsTrue) 
    { 
    myCollection.remove(anObjectsPartner); // throws ConcurrentModificationException 
    } 
} 

Спасибо.

+0

Возможный дубликат [Java: эффективный эквивалент удаления во время итерации коллекции] (http://stackoverflow.com/questions/223918/java-efficient-equivalent-to-removing-while-iterating-a-collection) – McDowell

ответ

8

Вы хотите удалить элемент из списка и продолжить итерацию в том же списке. Можете ли вы реализовать двухэтапное решение, в котором на шаге 1 вы собираете элементы, подлежащие удалению во временной коллекции, а на шаге 2 удаляете их после их идентификации?

9

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

Редактировать: Перечитав вопрос, этот подход не сработает, хотя я оставляю его здесь как пример того, как избежать этой проблемы в общем случае.

То, что вы хотите что-то вроде этого:

for (Iterator<BusinessObject> iter = myCollection.iterator; iter.hasNext();) 
{ 
    BusinessObject anObject = iter.next(); 
    if (someConditionIsTrue) 
    { 
     iter.remove(); 
    }   
} 

Если удалить объекты с помощью самого итератора, он знает о удалении и все работает, как и следовало ожидать. Обратите внимание, что, хотя я думаю, что все стандартные коллекции хорошо работают в этом отношении, итераторам не требуется для реализации метода remove(), поэтому, если у вас нет контроля над классом myCollection (и, следовательно, с классом реализации возвращаемого итератора), вы может потребоваться больше проверок безопасности.

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

Edit: Вы можете, вероятно, использовать эту последнюю технику, чтобы достичь того, чего вы хотите, но вы все равно в конечном итоге возвращается к причине, почему итераторы бросить исключение в первую очередь: Что следует итерация делать, если вам удалить элемент, который он еще не достиг? Удаление (или нет) текущего элемента относительно хорошо определено, но вы говорите об удалении партнера текущего элемента, который, как я полагаю, может быть в случайной точке в итерируемой. Поскольку нет четкого способа, которым это должно быть обработано, вам нужно будет предоставить некоторую форму логики, чтобы справиться с этим. В этом случае я склоняюсь к созданию и заполнению новой коллекции во время итерации, а затем назначая ее переменной myCollection в конце. Если это невозможно, то отслеживание элементов-партнеров для удаления и вызова myCollection.removeAll - это путь.

+0

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

+0

Да, я просто понял это и отредактировал соответствующим образом. Это делает ситуацию более липкой ... –

3

Некоторые мысли (это зависит от того, что именно отношения между двумя объектами в коллекции):

  1. карта с объектом в качестве ключа и партнера в качестве значения.
  2. A CopyOnWriteArrayList, но вы должны заметить, когда вы нажмете на партнера
  3. Сделайте копию в другой объект Collection и выполните итерацию по одному, удалив другую. Если эта оригинальная коллекция может быть набором, который будет полезен при удалении.
0

Почему бы не использовать коллекцию всего исходного объекта BusinessObject, а затем отдельный класс (например, карту), который их связывает (т. Е. Создает партнера)? Поместите их как составные элементы в свой собственный класс, чтобы вы всегда могли удалить партнера при удалении объекта Business. Не возлагайте ответственность за вызывающего абонента каждый раз, когда им нужно удалить объект BusinessObject из коллекции.

IE

class BusinessObjectCollection implements Collection<BusinessObject> { 
    Collection<BusinessObject> objects; 
    Map<BusinessObject, BusinessObject> associations; 

public void remove(BusinessObject o) { 
    ... 
// remove from collection and dissasociate... 
} 
} 
1

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

myCollection<BusinessObject> 
List<BusinessObject> deletedObjects = new ArrayList(myCollection.size()); 

for (BusinessObject anObject : myCollection) 
{ 
    if (!deletedObjects.contains(anObject)) 
    { 
     if (someConditionIsTrue) 
     { 
      deletedObjects.add(anObjectsPartner); 
     } 
    } 
} 
myCollection.removeAll(deletedObjects); 
1

CopyOnWriteArrayList будет делать то, что вы хотите.

0

Лучший ответ - второй, используйте итератор.