1

次のようなリストがあります。

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);

行 (マップ) の 3 つの要素 (「セクション」、「インデックス」) のうち 2 つが同じであることに基づいて、重複を削除する必要があります。これは私がやろうとしていることです:

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<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:「解決策」が意図したとおりに機能しませんでした。正解が選択されました。

4

6 に答える 6

4

Iteratorを介して反復する場合を除き、反復中にコレクションの要素を追加/削除することはできません。

Map でイテレータを取得するにはCollection#iterator()を参照してください。

反復処理中に Collection から要素を削除する方法については、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 ソースから)。

このクラスの iterator および 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 が回避されます。

于 2013-09-09T09:38:23.183 に答える
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;
        }
    }
}

内部反復を正確に構造化できない状況では、ループが開始する前に元のリストをコピーし、コピーを反復して安定した信頼性の高い反復を保証するのが最も簡単です。

于 2013-09-09T09:43:25.990 に答える
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 );
于 2013-09-09T09:42:58.693 に答える
0

これを行う 1 つの方法は、変更しようとしている 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);
        }
    }
}
于 2013-09-09T09:43:06.053 に答える
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 ;
      }

    }

    }
于 2013-09-09T09:58:09.153 に答える
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); 
}
于 2013-09-09T11:08:11.490 に答える