我有一个这样的清单:
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
Map<String, String> row;
row = new HashMap<String, String>();
row.put("page", "page1");
row.put("section", "section1");
row.put("index", "index1");
list.add(row);
row = new HashMap<String, String>();
row.put("page", "page2");
row.put("section", "section2");
row.put("index", "index2");
list.add(row);
row = new HashMap<String, String>();
row.put("page", "page3");
row.put("section", "section1");
row.put("index", "index1");
list.add(row);
我需要根据行(Map)中3个元素中的2个(“section”,“index”)删除重复项。这就是我想要做的事情:
for (Map<String, String> row : list) {
for (Map<String, String> el : list) {
if (row.get("section").equals(el.get("section")) && row.get("index").equals(el.get("index"))) {
list.remove(el);
}
}
}
它以java.util.ConcurrentModificationException
失败。必须有另一种方法,但我不知道如何。有什么想法吗?
更新我按照建议尝试使用Iterator仍然是同样的例外:
Iterator<Map<String, String>> it = list.iterator();
while (it.hasNext()) {
Map<String, String> row = it.next();
for (Map<String, String> el : list) {
if (row.get("section").equals(el.get("section")) && row.get("index").equals(el.get("index"))) {
list.remove(row);
}
}
}
UPDATE2:此操作失败并出现相同的异常:
Iterator<Map<String, String>> it = list.iterator();
while (it.hasNext()) {
Map<String, String> row = it.next();
Iterator<Map<String, String>> innerIt = list.iterator();
while (innerIt.hasNext()) {
Map<String, String> el = innerIt.next();
if (row.get("section").equals(el.get("section")) && row.get("index").equals(el.get("index"))) {
innerIt.remove();
//it.remove(); //fails as well
}
}
}
更新3,解决方案:非常简单:
for (int i = 0; i < list.size(); i++) {
for (int j = 0; j < list.size(); j++) {
if (list.get(i).get("section").equals(list.get(j).get("section")) && list.get(i).get("index").equals(list.get(j).get("index"))) {
list.remove(i);
}
}
}
更新4:“解决方案”未按预期工作。现在选择了正确的答案。
答案 0 :(得分:4)
除非您通过Iterator进行迭代,否则无法在迭代时添加/删除集合元素。
请参阅Collection#iterator()以获取地图上的迭代器。
请参阅Iterator#remove(),了解如何在迭代时从集合中删除元素。
您可以像这样构建代码:
//Get an iterator on your list.
Iterator<Map<String, String>> itr = list.iterator();
//iterate
while(itr.hasNext()) {
Map<String, String> elt= itr.next();
if(isDuplicate(list, elt)) {
itr.remove();
}
}
以下是查找是否有重复项的方法示例:
public boolean isDuplicate(List<Map<String, String>> list, Map<String, String> map){
//Count the occurences of the searched element.
int cpt = 0;
/*
* Here, another iterator is implicitly created.
* It is not the same as in the main loop.
* That's why I avoid the ConcurrentModificationException.
*/
for(Map<String, String> m : list) {
if(m.get("section").equals(map.get("section")) && m.get("index").equals(map.get("index"))) {
cpt++;
}
}
//If the element is found twice, then it is a duplicate.
return cpt == 2;
}
以下是方法ArrayList#remove()的Javadoc摘录(来自Sun JDK源代码):
此类的迭代器和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出一个ConcurrentModificationException。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒着任意的,非确定性行为的风险。
为了进一步了解迭代器的工作原理,让我们读一下ArrayList迭代器的Sun JDK源代码。这是ArrayList.java中的内部类:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
在这里,我们可以看到实例化时(使用Collection#iterator()),迭代器初始化expectedModCount
(modCount =修改计数)。这里,modCount
是类ArrayList的属性。
每次在迭代器(next()
,previous()
,add()
,remove()
)上调用方法时,都会调用此方法:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这是抛出 ConcurrentModificationException !
的方法每次对列表进行修改时,ArrayList都会更新modCount
。因此,如果您修改没有迭代器的列表,modCount
将变为!= expectedModCount
。在下一次调用迭代器的任何方法时,你会得到异常。
当你使用for-each循环时,你隐式创建一个迭代器并在每个循环结束时调用next()。
每次通过迭代器中的方法修改列表时,expectedModCount
都会更新为modCount
,从而避免出现ConcurrentModificationException。
答案 1 :(得分:1)
如果您明确使用Iterator,则可以从中删除。您无法将其与'foreach'循环或其他迭代器结合使用,除非在这种情况下,一旦找到匹配内部循环结束。
[注意:我修正了条件,因为它不排除自我匹配。]
Iterator<Map<String,String>> outerIt = list.iterator();
while (outerIt.hasNext()) {
Map<String,String> outer = outerIt.next();
for (Map<String, String> inner : list) {
if ((inner != outer) && outer.get("section").equals(inner.get("section")) && outer.get("index").equals(inner.get("index"))) {
// Match; de-dup.
// -- no longer iterating the 'inner' loop, so we don't need a copy.
outerIt.remove();
break;
}
}
}
对于无法精确构造内部迭代的情况,最简单的方法是在循环开始之前复制原始列表。只需重复复制即可确保稳定和安全。可靠的迭代。
答案 2 :(得分:0)
保留要删除的元素列表,然后删除它们。
List<Map<String, String> removeList = new List<Map<String, String>();
for (Map<String, String> row : list)
for (Map<String, String> el : list)
if (row.get("section").equals(el.get("section")) && row.get("index").equals(el.get("index")))
removeList.add( el );
for( Map< String, String > i : removeList )
list.remove( i );
答案 3 :(得分:0)
执行此操作的一种方法是制作要修改的HashMap的副本。迭代副本并更改原始副本。
试试这个......
Map<String, String> copyOfList = new HashMap<String, String>(list);
for (Map<String, String> row : copyOfList ) {
for (Map<String, String> el : copyOfList ) {
if (row.get("section").equals(el.get("section")) && row.get("index").equals(el.get("index"))) {
list.remove(el);
}
}
}
答案 4 :(得分:0)
您可以使用传统的for循环而不是使用其他循环。
for(int i=0;i<list.size();i++)
{
for(int j=0;j<list.size();j++)
{
if (list.get(i).get("section").equals(list.get(j).get("section")) && list.get(i).get("index").equals(list.get(j).get("index"))) {
list.remove(j);
j -= 1 ;
}
}
}
答案 5 :(得分:0)
这是一个简单的逻辑,不使用loop
或Iterator
,
您可以达到以下相同的效果,
public static List<Map<String, String>> removeDuplicate(
List<Map<String, String>> list) {
Set<Map<String, String>> set = new TreeSet<Map<String, String>>(
new Comparator<Map<String, String>>() {
@Override
public int compare(Map<String, String> o1,
Map<String, String> o2) {
// Your equals condition
if (o1.get("section").equals(o2.get("section"))
&& o1.get("index").equals(o2.get("index")))
return 0;
return -1;
}
});
set.addAll(list);
// convert back to list and return
return new ArrayList<Map<String, String>>(set);
}