Arraylist remove元素

àì夳堔傛蜴生んèń 2022-05-30 01:35 269阅读 0赞

Arraylist remove元素

用Arraylist 的remove方法删除元素时出现如下异常:

  1. 三月 01, 2018 2:41:59 下午 org.apache.catalina.core.StandardWrapperValve invoke
  2. 严重: Servlet.service() for servlet [user] in context with path [/jq] threw exception
  3. java.util.ConcurrentModificationException
  4. at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
  5. at java.util.ArrayList$Itr.next(ArrayList.java:851)
  6. at com.zjht.action.Add.delete(Add.java:151)
  7. at com.zjht.action.Add.doGet(Add.java:32)
  8. at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
  9. at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
  10. at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
  11. at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
  12. at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
  13. at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
  14. at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
  15. at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
  16. at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
  17. at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
  18. at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
  19. at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
  20. at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
  21. at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
  22. at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
  23. at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095)
  24. at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
  25. at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502)
  26. at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458)
  27. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  28. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  29. at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
  30. at java.lang.Thread.run(Thread.java:748)

我使用for-each 循环,如下代码:

  1. for (User user : userList) {
  2. if(user.getUserid() == userid){
  3. userList.remove(user);
  4. }
  5. }

从异常堆栈可以看出,是ArrayList的迭代器报出的异常,说明通过元素遍历集合时,实际上是使用迭代器进行访问的。

可为什么会产生这个异常呢?打断点单步调试进去发现,是这行代码抛出的异常:

  1. final void checkForComodification() {
  2. if (modCount != expectedModCount)
  3. throw new ConcurrentModificationException();
  4. }

modCount是集合修改的次数,当remove元素的时候就会加1,初始值为集合的大小。迭代器每次取得下一个元素的时候,都会进行判断,比较集合修改的次数和期望修改的次数是否一样,如果不一样,则抛出异常。查看集合的remove方法:

  1. private void fastRemove(int index) {
  2. modCount++;
  3. int numMoved = size - index - 1;
  4. if (numMoved > 0)
  5. System.arraycopy(elementData, index+1, elementData, index,
  6. numMoved);
  7. elementData[--size] = null; // clear to let GC do its work
  8. }

可以看到,删除元素时modCount已经加一,但是expectModCount并没有增加。所以在使用迭代器遍历下一个元素的时候,会抛出异常。那怎么解决这个问题呢?

其实使用迭代器自身的删除方法就没有问题了

  1. Iterator<User> iterator = userList.iterator();
  2. while(iterator.hasNext()){
  3. if(iterator.next().getUserid() == userid){
  4. iterator.remove();
  5. }
  6. }

查看迭代器自身的删除方法,果不其然,每次删除之后都会修改expectedModCount为modCount。这样的话就不会抛出异常

  1. public void remove() {
  2. if (lastRet < 0)
  3. throw new IllegalStateException();
  4. checkForComodification();
  5. try {
  6. ArrayList.this.remove(lastRet);
  7. cursor = lastRet;
  8. lastRet = -1;
  9. expectedModCount = modCount;
  10. } catch (IndexOutOfBoundsException ex) {
  11. throw new ConcurrentModificationException();
  12. }
  13. }

建议以后操作集合类的元素时,尽量使用迭代器。

可是还有一个地方不明白,modCount和expectedModCount这两个变量究竟是干什么用的?

为什么集合在进行操作时需要修改它?为什么迭代器在获取下一个元素的时候需要判断它们是否一样?

它们存在总是有道理的吧

发表评论

表情:
评论列表 (有 0 条评论,269人围观)

还没有评论,来说两句吧...

相关阅读

    相关 容易忽略的ArrayList.remove

    ArrayList.remove 方法是常用的一个移除集合里某个对象的方法。此方法使用起来非常方便。但是在使用的时候一定要注意当被移除的对象是int 类型的时候。确保正确使用这