假设我有一个整数集,并且我想增加集合中的每个整数。我该怎么做?
我可以在迭代时添加和删除集合中的元素吗?
在迭代原始集合时,是否需要创建一个新集合以将元素“复制并修改”到其中?
编辑:如果集合中的元素是不可变的呢?
Answers:
您可以使用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);
cannot
而不是can
?如,You cannot safely remove
而不是You can safely remove
?在第一段中似乎是矛盾的。
error: incompatible types: Object cannot be converted to Integer
如果使用迭代器对象遍历集合中的元素,则可以执行所需的操作。您可以随时删除它们,没关系。但是,在for循环中删除它们(每种类型的“标准”之一)都会给您带来麻烦:
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
除其他外,规定(强调我的):
“例如,通常不允许一个线程修改Collection而另一个线程对其进行迭代。通常,在这种情况下,迭代的结果是不确定的。某些Iterator实现(包括所有通用Collection的实现)如果检测到此行为,则JRE提供的实现可以选择抛出此异常”(...)
“请注意,此异常并不总是表示对象已被其他线程同时修改。如果单个线程发出违反对象契约的方法调用序列,则该对象可能会抛出此异常。例如,如果当线程使用快速故障迭代器迭代集合时,线程将直接修改集合,迭代器将抛出此异常。”
要详细介绍:可以在forEach循环中使用的对象需要实现“ java.lang.Iterable”接口(此处为javadoc )。这将产生一个Iterator(通过此接口中的“ Iterator”方法),该Iterator可按需实例化,并将在内部包含对从中创建Iterable对象的引用。但是,在forEach循环中使用Iterable对象时,该迭代器的实例对用户隐藏(您无法以任何方式自己访问它)。
这,再加上一个迭代器是非常有状态的,即为了使其魔术并对其“ next”和“ hasNext”方法产生连贯的响应,它需要支持对象不被迭代器本身改变进行迭代时,使其变为可运行状态,以便在对其进行迭代时,一旦检测到后备对象中发生了更改,它将立即引发异常。
Java将这种迭代称为“快速失败”迭代:即,有一些动作,通常是那些修改Iterable实例的动作(而Iterator对其进行迭代)。“快速失败”概念的“失败”部分是指迭代器检测这种“失败”动作何时发生的能力。的“快速失败”(和,这在我看来应该叫“尽力而为,快”),将终止通过ConcurrentModificationException的迭代的“快”的一部分,尽快,因为它可以检测到一个“失败”的动作有发生。
我不太喜欢迭代器的语义,请考虑将此作为选项。由于您发布的内部状态更少,因此也更安全
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;
}
您可以创建原始int的可变包装,并创建其中的一组:
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方法,但这不在此答案的范围内。
首先,我认为一次尝试做几件事通常是一种不好的做法,我建议您考虑一下要实现的目标。
虽然这是一个很好的理论问题,但从我的反馈中可以得出CopyOnWriteArraySet
,java.util.Set
接口的实现可以满足您的特殊要求。
http://download.oracle.com/javase/1,5.0/docs/api/java/util/concurrent/CopyOnWriteArraySet.html