简体   繁体   English

"如何在迭代时删除和添加元素到 TreeMap?"

[英]How to remove and add elements to TreeMap while iterating?

I want to write code like this -我想写这样的代码 -

for (Map.Entry<Long, Integer> e : map.entrySet()){
    map.remove(k);
    map.put(x, value);
}

Explanation why it caused ConcurrentModificationException 解释为什么导致ConcurrentModificationException

map.remove(k);
map.put(x, value);

for-each loop also internally create a iterator of the entrySet of map . for-each循环还在内部创建mapentrySet的迭代器。 While iterating over map you have modified the structure of the map by putting the value again to the map ( map.put(x,value) ) which cause this ConcurrentModificationException . 遍历map时,您通过将值再次放置到map( map.put(x,value)map.put(x,value)修改了map的结构,这会导致此ConcurrentModificationException

It is even well explained in documentation - 甚至在文档中也对此做了很好的解释-

The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. 此类的所有“集合视图方法”返回的迭代器都是快速失败的:如果在创建迭代器后的任何时间以任何方式对映射进行结构修改,则除了通过迭代器自己的remove方法之外,迭代器都会抛出ConcurrentModificationException 。 Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future. 因此,面对并发修改,迭代器会快速干净地失败,而不会在未来的不确定时间内冒任意,不确定的行为的风险。

How to solve this - 如何解决 -

you must change the change the structure of this map while iterating, you can insert this values later, like keep a temporary map and add this to the main map once iteration is finished his job. 您必须在迭代时更改此映射的结构更改,以后可以插入此值,例如保留一个临时映射,并在迭代完成后将其添加到主映射中。

Map<Long, Integer> tempMap = new HashMap<>();
for (Map.Entry<Long, Integer> e : map.entrySet()){
    map.remove(k);
    tempMap.put(x, value);
}
map.putAll(tempMap);

Iterate over a copy and you can add/remove just fine: 遍历一个副本,您可以添加/删除它:

for (Map.Entry<Long, Integer> e : new LinkedHashMap<Long, Integer>(map).entrySet()){
    map.remove(k);
    map.put(x, value);
}

It's not even any more lines of code, because the copy ims made in-line via the copy constructor. 甚至没有更多的代码行,因为复制im是通过复制构造函数在线生成的。 LinkedHashMap was chosen to preserve iteration order (if that matters). 选择LinkedHashMap可以保留迭代顺序(如果重要)。

A sample code snippet for removing an element from the map is given below. 下面给出了用于从地图中删除元素的示例代码段。

for(Iterator<Map.Entry<Long, Integer>> it = map.entrySet().iterator();it.next();)
{
  Map.Entry<String, String> entry = it.next();
 if(//some logic)    
 it.remove();
}

If your code involves a lot of addition and removal , you might just want to use ConcurrentHashMap. 如果您的代码涉及大量添加和删除,则可能只想使用ConcurrentHashMap。 ConcurrentHashMap 并发哈希图

You will have to create a copy of your map using copy constructor. 您将必须使用副本构造函数创建地图的副本。 Now iterate on 1 and modify second map. 现在迭代1并修改第二张地图。 I am assuming that you will not need to iterate newly added value as it wont make much sense. 我假设您不需要迭代新增加的价值,因为这没有多大意义。

You can achieve your task by creating a copy is because the keys will remain same in both. 您可以通过创建副本来完成任务,因为这两个键将保持相同。

EDIT: 编辑:

I dont think its a good idea to iterate the newly added element to a Hashmap. 我认为将新添加的元素迭代到Hashmap并不是一个好主意。 If you check the api's provided by Iterator then you will find only remove method, there is no add method in it. 如果检查Iterator提供的api,则只会找到remove方法,而没有add方法。 There is a reason behind this and you can check javadoc for this. 这是有原因的,您可以为此检查javadoc。 Now coming to the point, on how to iterate newly added element. 现在到了如何迭代新添加的元素的地步。

  1. Create a copy of your HashMap . 创建HashMap的副本。 So you will iterate one and modify the the other Map . 因此,您将迭代一个,并修改另一个Map
  2. As the requirement is to both add and remove elements in Map , i would like to use ListIterator for this [this is different from normal Iterator ]. 由于要求同时在Map添加和删​​除元素,因此我想为此使用ListIterator [这与常规Iterator不同]。
  3. I will get the keyset of Map1 and convert it to a list using ArrayList(Collection<? extends E> c) . 我将获得Map1的keyset ,并使用ArrayList(Collection<? extends E> c)将其转换为列表。
  4. Now i will get ListIterator from List created in step 3, and add, remove elements in ListIterator as well as in Map2 [Remeber you need to add , remove both in ListIterator and Map2]. 现在,我将获得ListIteratorList步骤3中创建,并在添加,删除元素ListIterator ,以及在地图2 [Remeber您需要添加,删除中双方ListIterator和地图2]。

Because you can't do that. 因为你做不到。

An easy solution is to use another temporary map where you put the values you want and finally switch pointers with the original one (ie Map = newMap ) 一种简单的解决方案是使用另一个临时映射,在其中放置所需的值,最后用原始映射切换指针(即Map = newMap)

Try going through the map as follows尝试按如下方式浏览地图

while (tree_map.size() > 0){
     // if k is key
     if (tree_map.containsKey()){
        tree_map.remove(k);
     }
     tree_map.put(x, value);
     break;
     // can go through the for loop or other code as per requirements
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM