迭代器删除/编辑是安全的,但我还需要编辑以下所有迭代

时间:2013-10-17 13:54:27

标签: java optimization iterator concurrentmodification

我已经在stackflow中阅读了一些关于ConcurrentModificationException的内容,我的实际更新似乎不是问题,它可能是我设计中的问题,或者我需要一种我尚未学习的技术。

示例情况: 我的迭代器正在运行位置标记。 然后可以执行动作以将标记移位(例如,插入到字符串中)。 所有大于当前位置的标记也必须移动以保持正确性。

任务: 如何在没有迭代器爆炸的情况下更新剩余的标记? 我可以刷新迭代器,还是再次中断并启动循环?

以下代码摘自我的工作。

 public void innerLoop(Boolean b) {
    //An Example of what I'm working with
    HashMap<String, HashSet<Integer>> map = new HashMap<String, HashSet<Integer>>() {
        {
            put("Nonce",
                new HashSet<Integer>() {

                {
                    add(1);
                    add(2);
                    add(3);
                    add(4);
                    add(5);
                }
            });
        }
    };

    //for each key
    for (String key: map.keySet()) {
        HashSet<Integer> positions = map.get(key);

        //for each integer
        for (Iterator<Integer> it = positions.iterator(); it.hasNext();) {
            Integer position = it.next();

            System.out.println("position =" + position);
            //(out of scope) decision requiring elements from the outter loops
            if (new Random().nextBoolean()&&b) {
                //shift position by +4 (or whatever)
                //and every other (int >= position)
                System.out.println("Shift " + position + " by 4");
                Integer shift = 4;
                update(position,
                       shift,
                       positions);
                it.remove();
            }
        }
    }
}

public void update(Integer current,
                   Integer diff,
                   Set<Integer> set) {

    if (set != null) {
        HashSet<Integer> temp = new HashSet<Integer>();
        for (Integer old: set) {
            if (old >= current) {
                temp.add(old);
                System.out.println(old + "Added to temp");
            }
        }

        for (Integer old: temp) {
            set.remove(old);
            System.out.println(old + "removed");
            set.add(old + diff);
            System.out.println((old + diff) + "Added");
        }
    }
}

使用Garrett Hall解决方案编辑

 public void nestedloops() {

    HashMap<String, HashSet<Integer>> map = new HashMap<String, HashSet<Integer>>() {
        {
            put("Hello",
                new HashSet<Integer>() {

                {
                    add(5);
                    add(2);
                    add(3);
                    add(4);
                    add(1);
                    add(6);
                }
            });
        }
    };

    //for each key
    for (String key: map.keySet()) {
        ArrayList<Integer> positions = new ArrayList<Integer>(map.get(key));
        //for each integer
        for (int i = 0; i < positions.size(); i++) {
            Integer position = positions.get(i);
            System.out.println("[" + i + "] =" + position);
            //out of scope decision
            if (new Random().nextBoolean()) {
                //shift position by +4
                //and every other (int >= position)
                System.out.println("Shift after " + position + " by 4");
                Integer shift = 4;
                //Update the array
                for (int j = 0; j < positions.size(); j++) {
                    Integer checkPosition = positions.get(j);
                    if (checkPosition > position) {
                        System.out.println(checkPosition + "increased by 4");
                        positions.set(j,
                                      checkPosition + shift);
                    }
                }
            }
        }
        //Add updated Array
        map.put(key,
                new HashSet<Integer>(positions));
    }
}

2 个答案:

答案 0 :(得分:1)

最好的办法是将HashSet编入索引列表。然后,您可以使用索引来引用元素而不是Iterator。只要您不删除或添加(仅更新)元素,那么您的索引将是正确的。否则你将不得不考虑到这一点。例如:

ArrayList<Integer> positions = new ArrayList<Integer>(map.get(key));
for (int i = 0; i < positions.size(); i ++) {
  // updating list
  for (int j = i; i < positions.size(); j ++) {
    positions.set(j, positions.get(i) + diff);
  }
}

答案 1 :(得分:0)

我会将原始集复制到列表中,这样您就不必担心当前的迭代代码了。然后更新辅助列表(不进行迭代)。

原因:

  1. 您无法一次迭代和修改原始收藏(ConcurrentModificationExceptions无法解决)
  2. 很好one liner可以移动列表中的项目。

    Collections.rotate(list.subList(j, k+1), -1);
    
  3. Guava将能够处理“查找满足该谓词并转换列表的第一个索引”,这是一堆实用方法。