Powiedzmy, że mam zestaw liczb całkowitych i chcę zwiększyć każdą liczbę całkowitą w zestawie. Jak bym to zrobił?
Czy mogę dodawać i usuwać elementy z zestawu podczas iteracji?
Czy musiałbym utworzyć nowy zestaw, do którego „skopiowałbym i zmodyfikował” elementy podczas iteracji oryginalnego zestawu?
EDYCJA: A co jeśli elementy zestawu są niezmienne?
cannot
raczej niżcan
? AYou cannot safely remove
raczej niżYou can safely remove
? Wydaje się, że to sprzeczność w tym pierwszym akapicie.error: incompatible types: Object cannot be converted to Integer
Możesz robić, co chcesz, jeśli użyjesz obiektu iteratora do przejrzenia elementów w swoim zestawie. Możesz je usunąć w drodze i wszystko w porządku. Jednak usunięcie ich w pętli for (albo „standardowej”, albo dla każdego rodzaju) spowoduje kłopoty:
Set<Integer> set = new TreeSet<Integer>(); set.add(1); set.add(2); set.add(3); //good way: Iterator<Integer> iterator = set.iterator(); while(iterator.hasNext()) { Integer setElement = iterator.next(); if(setElement==2) { iterator.remove(); } } //bad way: for(Integer setElement:set) { if(setElement==2) { //might work or might throw exception, Java calls it indefined behaviour: set.remove(setElement); } }
Zgodnie z komentarzem @ mrgloom, oto więcej szczegółów wyjaśniających, dlaczego „zły” sposób opisany powyżej jest… cóż… zły:
Bez wchodzenia w zbyt wiele szczegółów na temat tego, jak Java implementuje to, na wysokim poziomie, możemy powiedzieć, że „zły” sposób jest zły, ponieważ jest jasno określony jako taki w dokumentacji Java:
https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html
zastrzegam między innymi, że (wyróżnienie moje):
Aby przejść do szczegółów: obiekt, który może być użyty w pętli forEach, musi zaimplementować interfejs „java.lang.Iterable” ( tutaj javadoc ). Tworzy to Iterator (za pomocą metody „Iterator” znajdującej się w tym interfejsie), który jest tworzony na żądanie i będzie zawierał wewnętrznie odniesienie do obiektu Iterowalnego, z którego został utworzony. Jednak gdy w pętli forEach jest używany obiekt iterowalny, instancja tego iteratora jest ukryta dla użytkownika (nie można uzyskać do niego w żaden sposób dostępu samodzielnie).
To, w połączeniu z faktem, że Iterator jest dość stanowy, tj. Aby wykonać swoją magię i mieć spójne odpowiedzi na swoje metody "next" i "hasNext", potrzebuje, aby obiekt bazowy nie został zmieniony przez coś innego niż sam iterator podczas iteracji sprawia, że zgłasza wyjątek, gdy tylko wykryje, że coś się zmieniło w obiekcie bazowym podczas iteracji po nim.
Java nazywa tę iterację „odporną na awarie”: tj. Istnieją pewne akcje, zwykle takie, które modyfikują instancję iterowalną (podczas gdy Iterator ją iteruje). Część „niepowodzenia” pojęcia „szybkiego niepowodzenia” odnosi się do zdolności Iteratora do wykrywania, kiedy takie „niepowodzenie” ma miejsce. „Szybka” część „fail-fast” (a, który moim zdaniem powinien być nazywany „best-effort-fast”), zakończy iteracji poprzez ConcurrentModificationException tak szybko, jak to może wykryć , że „nie” działanie ma zdarzyć.
źródło
Nie lubię zbytnio semantyki iteratora, rozważ to jako opcję. Jest to również bezpieczniejsze, ponieważ publikujesz mniej informacji o swoim stanie wewnętrznym
private Map<String, String> JSONtoMAP(String jsonString) { JSONObject json = new JSONObject(jsonString); Map<String, String> outMap = new HashMap<String, String>(); for (String curKey : (Set<String>) json.keySet()) { outMap.put(curKey, json.getString(curKey)); } return outMap; }
źródło
Możesz stworzyć zmienną otokę pierwotnego int i stworzyć zbiór tych:
class MutableInteger { private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } } class Test { public static void main(String[] args) { Set<MutableInteger> mySet = new HashSet<MutableInteger>(); // populate the set // .... for (MutableInteger integer: mySet) { integer.setValue(integer.getValue() + 1); } } }
Oczywiście, jeśli używasz HashSet, powinieneś zaimplementować metodę hash, equals w swoim MutableInteger, ale to wykracza poza zakres tej odpowiedzi.
źródło
Po pierwsze, uważam, że próba zrobienia kilku rzeczy na raz jest ogólnie złą praktyką i sugeruję, abyś przemyśleł, co próbujesz osiągnąć.
Jest to jednak dobre pytanie teoretyczne iz tego, co sądzę,
CopyOnWriteArraySet
implementacjajava.util.Set
interfejsu spełnia Twoje raczej specjalne wymagania.http://download.oracle.com/javase/1,5.0/docs/api/java/util/concurrent/CopyOnWriteArraySet.html
źródło