Java 在for循环中使用迭代器从ArrayList中删除元素

Java 在for循环中使用迭代器从ArrayList中删除元素,java,arraylist,iterator,Java,Arraylist,Iterator,我有一个以版本号作为字段的对象数组列表。我想在ArrayList上做一些工作,但我只想要对象的最新版本。我在考虑这样编码: ArrayList<ObjectVO> ObjectList = getObjectList(); for(ObjectVO myVO : ObjectList) { Iterator<ObjectVO> iter = ObjectList.iterator(); while(iter.hasNext()) { ObjectVO

我有一个以版本号作为字段的对象数组列表。我想在ArrayList上做一些工作,但我只想要对象的最新版本。我在考虑这样编码:

ArrayList<ObjectVO> ObjectList = getObjectList();
for(ObjectVO myVO : ObjectList) {
  Iterator<ObjectVO> iter = ObjectList.iterator();

  while(iter.hasNext()) {
     ObjectVO checkVO = iter.next();
     if(myVO.getID().equals(checkVO.getID()) {
         //they are the same object ID.  Check the version number, remove it lower
         if(myVO.getVersion() > checkVO.getVersion()) {
              iter.remove();
         }
      }
   }
 }
ArrayList ObjectList=getObjectList();
for(ObjectVO myVO:ObjectList){
迭代器iter=ObjectList.Iterator();
while(iter.hasNext()){
ObjectVO checkVO=iter.next();
如果(myVO.getID().equals)(checkVO.getID()){
//它们是相同的对象ID。请检查版本号,然后将其删除
if(myVO.getVersion()>checkVO.getVersion()){
iter.remove();
}
}
}
}

这有效吗?我不知道我们最初处于for循环中的事实是否会在运行时破坏ArrayList的易变性。

不,这不起作用。
iter.remove()
将导致out-for循环失败,出现
ConcurrentModificationException

您可以使用索引for循环和
位集来跟踪要删除的内容,而不是执行此操作:

BitSet toRemove = new BitSet();
for (int m = 0; m < ObjectList.size(); ++m) {
  if (toRemove.get(m)) continue;
  ObjectVO myVO = ObjectList.get(m);

  for (int c = 0; c < ObjectList.size(); ++c) {
    if (toRemove.get(c)) continue;
    ObjectVO checkVO = ObjectList.get(c);

    if(myVO.getID().equals(checkVO.getID()) {
      //they are the same object ID.  Check the version number, remove it lower
      if(myVO.getVersion() > checkVO.getVersion()) {
          toRemove.set(c);
      }
    }
  }
}

假设您有内存,而不是执行O(N^2)操作,您可以通过使用映射跟踪每个Id的最新版本来更有效地执行此操作(O(N))。一次通过跟踪每个Id的最新版本,第二次通过删除不是最新的元素

Map<Integer, Thing> newestById = new HashMap<>();

for (Thing thing : list) {
  newestById.merge(thing.id, thing, (a,b) -> a.version > b.version ? a : b);
}
list.removeIf(thing -> thing != newestById.get(thing.id)); }
Map newestById=newhashmap();
对于(事物:列表){
newestById.merge(thing.id,thing,(a,b)->a.version>b.version?a:b);
}
list.removeIf(thing->thing!=newestById.get(thing.id));}

根据您的使用情况,您甚至可以将数据存储在映射中而不是列表中,并在将其添加到映射中之前检查版本是否为最新版本。

其他答案已经讨论过,但这不起作用。在我看来,您有三个选项,以内存换取CPU周期/灵活性。我在中使用了Integer而不是ObjectVO我举了一些例子,但交换它们是微不足道的

选项1-中等内存,阵列单次通过

跟踪您看到的最高ID,并在满足条件时使用新项目填充ArrayList。当遇到新的更高ID时,请丢弃ArrayList并创建一个新的ArrayList:

ArrayList<Integer> objectList = getObjectList();
Integer bestId = -1;
ArrayList<Integer> allObjectsMatchingId = new ArrayList<>();
for(Integer currentObject : objectList) {
    if(currentObject > bestId) {
        bestId = currentObject;
        allObjectsMatchingId = new ArrayList<>();
    } else if(currentObject == bestId) {
        allObjectsMatchingId.add(currentObject);
    }
}
return allObjectsMatchingId;
ArrayList objectList=getObjectList();
整数bestId=-1;
ArrayList allObjectsMatchingId=新ArrayList();
对于(整数currentObject:objectList){
如果(currentObject>bestId){
bestId=currentObject;
allObjectsMatchingId=new ArrayList();
}else if(currentObject==bestId){
allObjectsMatchingId.add(currentObject);
}
}
返回allObjectsMatchingId;
选项2-更昂贵的内存,阵列单通道,最灵活

对于您看到的每个ID,创建一个ArrayList并将其存储在映射中。这使您可以轻松地更改有关要保留的ID的条件

ArrayList<Integer> objectList = getObjectList();
Map<Integer, ArrayList<Integer>> objectsById = new HashMap<>();
for(Integer currentObject : objectList) {
    ArrayList<Integer> listForId = objectsById.get(currentObject);
    if(listForId == null) {
        listForId = new ArrayList<Integer>();
    }
    listForId.add(currentObject);
    objectsById.put(currentObject, listForId);
}
Integer bestId = -1;
for(Integer i : objectsById.keySet()) {
    if(i > bestId) {
        bestId = i;
    }
}
return objectsById.get(bestId);
ArrayList objectList=getObjectList();
Map objectsById=new HashMap();
对于(整数currentObject:objectList){
ArrayList listForId=objectsById.get(currentObject);
if(listForId==null){
listForId=新的ArrayList();
}
添加(当前对象);
objectsById.put(currentObject,listForId);
}
整数bestId=-1;
对于(整数i:objectsById.keySet()){
如果(i>bestId){
bestId=i;
}
}
返回objectsById.get(bestId);
选项3-除了id之外,没有额外的内存,阵列的两次传递

在ArrayList中搜索最高ID,然后将数组筛选为仅通过该筛选的元素

这是最接近您当前的实现,不同之处在于您可以在单独的步骤中完成它们。这将复杂性从O(N^2)降低到O(N),并且在迭代ArrayList时不修改它是有效的。如果您与Java 8兼容,您可以在此处使用流而不是迭代器进行筛选。请参阅

ArrayList objectList=getObjectList();
整数bestId=-1;
对于(整数currentObject:objectList){
如果(currentObject>bestId){
bestId=currentObject;
}
}
迭代器iter=objectList.Iterator();
while(iter.hasNext()){
if(iter.next()!=bestId){
iter.remove();
}
}

为什么不使用Java Streams来解决这个问题:

Collection<ObjectVO> result = objectList.stream()
        .collect(Collectors.toMap(ObjectVO::getID, Function.identity(),
                BinaryOperator.maxBy(Comparator.comparing(ObjectVO::getVersion))))
        .values();
Collection result=objectList.stream()
.collect(Collectors.toMap(ObjectVO::getID,Function.identity(),
maxBy(Comparator.comparing(ObjectVO::getVersion)))
.values();
这将创建一个映射,其中包含每个id的最大版本。然后您可以使用
map.values()
获取对象列表


如果您需要
列表
数组列表
,您可以使用
新建数组列表(结果)

否,这不起作用。
iter.remove()
将导致out-for循环因
ConcurrentModificationException
而失败@AndyTurner我可以用另一个迭代器替换第一个for循环,并让两个迭代器并行运行吗?您必须在外循环中使用迭代器,在内循环中使用增强的for循环。我想这应该可以。@DorianGray no、 这也会中断。@user3334871是否要保留版本号最高的东西?
ArrayList<Integer> objectList = getObjectList();
Map<Integer, ArrayList<Integer>> objectsById = new HashMap<>();
for(Integer currentObject : objectList) {
    ArrayList<Integer> listForId = objectsById.get(currentObject);
    if(listForId == null) {
        listForId = new ArrayList<Integer>();
    }
    listForId.add(currentObject);
    objectsById.put(currentObject, listForId);
}
Integer bestId = -1;
for(Integer i : objectsById.keySet()) {
    if(i > bestId) {
        bestId = i;
    }
}
return objectsById.get(bestId);
ArrayList<Integer> objectList = getObjectList();
Integer bestId = -1;
for(Integer currentObject : objectList) {
    if(currentObject > bestId) {
        bestId = currentObject;
    }
}
Iterator<Integer> iter = objectList.iterator();
while(iter.hasNext()) {
    if(iter.next() != bestId) {
        iter.remove();
    }
}
Collection<ObjectVO> result = objectList.stream()
        .collect(Collectors.toMap(ObjectVO::getID, Function.identity(),
                BinaryOperator.maxBy(Comparator.comparing(ObjectVO::getVersion))))
        .values();