2012-06-04 5 views
11

Enumeration не выбрасывает ConcurrentModificationException, почему?Перечисление Java против Iterator

См. Ниже код.

public static void main(String[] args) { 

    Vector<String> v=new Vector<String>(); 
    v.add("Amit"); 
    v.add("Raj"); 
    v.add("Pathak"); 
    v.add("Sumit"); 
    v.add("Aron"); 
    v.add("Trek"); 

    Enumeration<String> en=v.elements(); 

    while(en.hasMoreElements()) 
    { 
     String value=(String) en.nextElement(); 
     System.out.println(value); 
     v.remove(value); 

    } 

} 

Это только печатает:

 
Amit 
Pathak 
Aron 

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

Редактировать: При работе с Iterator он выбрасывает ConcurrentModificationException в однопоточном приложении.

public static void main(String[] args) { 

    Vector<String> v=new Vector<String>(); 
    v.add("Amit"); 
    v.add("Raj"); 
    v.add("Pathak"); 
    v.add("Sumit"); 
    v.add("Aron"); 
    v.add("Trek"); 

    Iterator<String> it=v.iterator(); 
    while(it.hasNext()) 
    { 
     String value=(String) it.next(); 
     System.out.println(value); 
     v.remove(value); 
    } 
} 

Пожалуйста, проверьте.

+1

Пожалуйста, ознакомьтесь с http://stackoverflow.com/questions/948194/different-using-java-util-enumeration-and-iterator – Garbage

+0

Что предыстория этого вопроса? Вы рассматриваете использование перечислений вместо итераторов в многопоточной среде? –

+0

Есть ли еще код, о котором мы должны знать? В приведенном выше примере нет многопоточности, поэтому, если у вас нет конкретной причины винить безопасность потоков, я думаю, что вы немного внедорожники здесь – posdef

ответ

4

Перечисления не бросает ConcurrentModificationException, почему?

Поскольку в вызываемом коде нет пути, который выдает это исключение. Редактировать: Я имею в виду реализацию, предоставляемую классом Vector, а не интерфейс Enumeration вообще.

Почему такое поведение. Можем ли мы сказать, что Enumerator является потокобезопасным.

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

Причина вашего вывода в том, что объект Enumeration поддерживает счетчик, который увеличивается после каждой активации nextElement(). Этот счетчик не знает о вашем обращении remove().

+0

Мое понимание - это перечисление имеет ссылку на коллекцию, как у Iterator. Как только мы удаляем элемент из вектора, он замечается в Enumeration. Тогда почему он печатает 3 не все 5, если мы сначала печатаем, а затем удаляем. – amicngh

+0

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

-1

удалить v.remove(value) и все будет работать, как ожидалось

Edit: извините, неправильно вопрос там

Это не имеет ничего общего с threadsafety хотя. Вы даже не многопоточны, поэтому нет причин, по которым Java создаст исключение.

Если вы хотите исключения, когда вы меняете вектор сделать неизменяемым

+4

Это, похоже, не ответ на заданный вопрос –

+0

Downvote удален после редактирования .. –

10

Обратите внимание, что ConcurrentModificationException не имеет ничего общего с параллелизмом в смысле многопоточности или безопасности потоков. Некоторые коллекции допускают параллельные модификации, некоторые - нет. Обычно вы можете найти ответ в документах. Но одновременное не означает одновременно разные потоки. Это означает, что вы можете изменить коллекцию во время ее повтора.

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

В любом случае, если вы используете один поток для итерации и изменения коллекции, ConcurrentHashMap является неправильным решением вашей проблемы. Вы неправильно используете API. Вы должны использовать Iterator.remove() для удаления элементов. Кроме того, вы можете сделать копию коллекции, прежде чем выполнять итерацию и изменение оригинала.

EDIT:

Я не знаю ни одного Перечисления, что бросает ConcurrentModificationException. Однако поведение в случае одновременной модификации может быть не таким, как вы ожидаете. Как вы видите в этом примере, перечисление пропускает каждый второй элемент в списке. Это связано с тем, что внутренний индекс увеличивается, независимо от удалений. Так это то, что происходит:

  • en.nextElement() - возвращает первый элемент вектора, увеличивает индекс до 1
  • v.remove (значение) - удаляет первый элемент из вектора, сдвигает все элементы оставили
  • en.nextElement() - возвращает второй элемент из вектора, который в настоящее время является «Pathak»

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

Iterator<String> it=v.iterator(); 
while(it.hasNext()) 
{ 
    String value=(String) it.next(); 
    System.out.println(value); 
    it.remove(); // not v.remove(value); !! 
} 

В качестве альтернативы:

for(String value : new Vector<String>(v)) // make a copy 
{ 
    String value=(String) it.next(); 
    System.out.println(value); 
    v.remove(value); 
} 

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

+0

И, наконец, я понимаю, что Enumeration никогда не бросает ConcurrentModificationException? – amicngh

+1

+1 для «ConcurrentModificationException не имеет ничего общего с параллелизмом в смысле многопоточности или безопасности потоков» –

3

Параллельная модификация здесь не имеет ничего общего с темами.

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

Итераторы и перечисления коллекций могут в этом случае делать ConcurrentModificationException, но это необязательно. Те, которые показывают, показывают неустойчивое поведение. По-видимому, перечисление Vector не обязательно быстро.

Thread-safety, очевидно, имеет несколько потоков. Vector является потокобезопасным только в том смысле, что его операции (такие как add, get и т. Д.) Синхронизируются. Это делается для того, чтобы избежать детерминированного поведения, когда один поток добавляет элемент, в то время как другой поток пытается удалить его, например.

Теперь, когда один поток структурно изменяет вашу коллекцию, в то время как другой поток выполняет итерацию по ней, вам приходится иметь дело как с проблемами безопасности потоков, так и с одновременной модификацией. В этом случае, и, возможно, в целом, безопаснее не полагаться на ConcurrentModificationException. Лучше всего выбрать соответствующую реализацию коллекции (например, поточно-безопасную) и самостоятельно избегать/запрещать одновременную модификацию.

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

1

Короткий ответ: это bonus feature, который был изобретен после того, как перечисление уже было там, поэтому тот факт, что перечислитель не бросает его, не предлагает ничего конкретного.

Длинный ответ:
Из википедии: реализациях

Коллекция в предварительной версии JDK 1.2 [...] не содержат рамки в коллекции. Стандартные методы группировки объектов Java осуществлялись через классы массива, Vector и Hashtable, , которые, к сожалению, были нелегкими для расширения, и не реализовали стандартный интерфейс участника . Чтобы устранить необходимость в многоразовых структурах данных коллекции [...] Рамка коллекций была разработана и разработана в основном Джошуа Блох и была представлена ​​в JDK 1.2.

Когда команда Блох сделал это, они думали, что это была хорошая идея поставить в новый механизм, который рассылает сигнал тревоги (ConcurrentModificationException), когда их коллекции не были синхронизированы должным образом в многопоточной программе. Есть две важные вещи, которые следует отметить в отношении этого механизма: 1) не гарантируется обнаружение ошибок параллелизма - исключение вызывается только в том случае, если вам повезет. 2) Исключение также вызывается, если вы неправильно используете коллекцию с использованием одного потока (как в вашем примере).

Таким образом, коллекция, не бросающая ConcurrentModificationException при доступе несколькими потоками, не означает, что она также является поточно-безопасной.

0

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

import java.util.*; 

public class ConcurrencyTest { 
    public static void main(String[] args) { 

     Vector<String> v=new Vector<String>(); 
     v.add("Amit"); 
     v.add("Raj"); 
     v.add("Pathak"); 
     v.add("Sumit"); 
     v.add("Aron"); 
     v.add("Trek"); 

     Enumeration<String> en=Collections.enumeration(v);//v.elements(); 

     while(en.hasMoreElements()) 
     { 
      String value=(String) en.nextElement(); 
      System.out.println(value); 
      v.remove(value); 
     }    

     System.out.println("************************************"); 

     Iterator<String> iter = v.iterator(); 
      while(iter.hasNext()){ 
       System.out.println(iter.next()); 
       iter.remove(); 
       System.out.println(v.size()); 
     } 
    } 
} 

Перечисление только интерфейс, это фактическое поведение зависит от реализации. Перечисление из вызова Collections.enumeration() в какой-то момент обертывает итератор, поэтому оно действительно не выполняется, но перечисление, полученное от вызова Vector.elements(), не является.

Непросроченное перечисление может ввести произвольное, недетерминированное поведение в неопределенное время в будущем. Например: если вы напишете основной метод как это, он будет вызывать java.util.NoSuchElementException после первой итерации.

public static void main(String[] args) { 

     Vector<String> v=new Vector<String>(); 
     v.add("Amit"); 
     v.add("Raj"); 
     v.add("Pathak"); 

     Enumeration<String> en = v.elements(); //Collections.enumeration(v); 

     while(en.hasMoreElements()) 
     { 
      v.remove(0); 
      String value=(String) en.nextElement(); 
      System.out.println(value); 
     }  
    } 
Смежные вопросы