我如何迭代和修改Java集?
比方说,我有一个整数集,我想增加集中的每个整数。 我将如何做到这一点?
我是否允许在迭代中添加和删除元素?
我是否需要创build一个新的集合,以便在复制原始集合的同时“复制和修改”这些元素?
编辑:如果该集合的元素是不可变的?
迭代期间,您可以安全地从一个集合中移除一个Iterator对象; 试图在迭代时通过其API修改集合将会破坏迭代器。 Set类通过getIterator()提供一个迭代器。
但是,Integer对象是不可变的; 我的策略是遍历整个集合,对于每个整数i,将i + 1添加到一些新的临时集合中。 完成迭代后,从原始集合中删除所有元素,并添加新临时集合的所有元素。
Set<Integer> s; //contains your Integers ... Set<Integer> temp = new Set<Integer>(); for(Integer i : s) temp.add(i+1); s.clear(); s.addAll(temp);
如果你使用迭代器对象来遍历你的集合中的元素,你可以做你想做的事情。 你可以在旅途中删除他们没关系。 但是,在for循环中删除它们(无论是“标准”还是每种types)都会给你带来麻烦:
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); } }
根据@ mrgloom的评论,这里有更多的细节,为什么上面描述的“坏”的方式是好的,坏的:
没有深入了解Java如何实现这一点,在很大程度上,我们可以说“坏”的方式是不好的,因为它在Java文档中明确规定:
https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html
规定,除其他外,(重点是我的):
“ 例如,一个线程一般不允许修改一个集合,而另一个线程正在迭代它,通常迭代的结果在这些情况下是不确定的,一些迭代器实现(包括所有通用集合由JRE提供的实现)可以select抛出这个exception,如果检测到这种行为“(…)
“ 注意,这个exception并不总是表明一个对象已经被不同的线程同时修改了,如果一个线程发出一系列违反对象约定的方法调用,对象可能会抛出这个exception,例如if一个线程直接修改一个集合,而它正在使用快速迭代器迭代集合,迭代器将抛出这个exception。
更多细节:可以在forEach循环中使用的对象需要实现“java.lang.Iterable”接口( 这里是 javadoc)。 这会产生一个Iterator (通过在这个接口中find的“Iterator”方法),它会根据需要实例化,并且在内部包含对它创build的Iterable对象的引用。 但是,当在forEach循环中使用Iterable对象时,此迭代器的实例对用户是隐藏的(不能以任何方式自己访问它)。
这一点,再加上迭代器是非常有状态的事实,也就是说,为了做到它的魔力,并为其“下一个”和“下一个”方法做出连贯的响应,它需要支持对象不会被迭代器本身以外的东西改变而它正在迭代,所以它会在它检测到在支持对象正在迭代它时发生了某些变化的情况下立即抛出exception。
Java把这种“失败 – 快速”迭代称为:即有一些动作,通常是那些修改一个Iterable实例(而Iterator迭代它)的动作。 “失败”概念的“失败”部分是指迭代器检测何时发生这种“失败”行为的能力。 “快速”(在我看来应该被称为“尽力而为”)的“快速”部分只要能够检测到“失败”动作就会通过ConcurrentModificationException终止迭代发生。
我不太喜欢迭代器的语义,请考虑这个选项。 发布较less的内部状态也更安全
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; }
你可以创build一个可变的原始int包装并创build一个这样的包装:
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); } } }
当然,如果你使用的是HashSet,你应该在你的MutableInteger中实现hash,equals方法,但这不在这个答案的范围之内。
首先,我认为一次尝试做几件事是一个不好的做法,我build议你考虑一下你想达到的目标。
它作为一个很好的理论问题,并从我收集java.util.Set
接口的CopyOnWriteArraySet
实现满足您相当特殊的要求。
http://download.oracle.com/javase/1,5.0/docs/api/java/util/concurrent/CopyOnWriteArraySet.html