HashMap和List遍历方法及如何遍历删除元素总结

 更新时间:2019年05月31日 10:14:03   投稿:laozhang  
在本篇文章中小编给大家分享了关于HashMap和List遍历方法及如何遍历删除元素知识点总结,需要的朋友们参考下。

相信大家对集合遍历再熟悉不过了,这里总结一下HashMap和List的遍历方法,以及它们该如何实现遍历删除。

这里对于每种遍历删除出现的问题的原因都给出了详解!

(一)List的遍历方法及如何实现遍历删除

我们造一个list出来,接下来用不同方法遍历删除,如下代码:

List<String> list= new ArrayList<String>();
  famous.add("zs");
  famous.add("ls");
  famous.add("ww");
  famous.add("dz");

1、for循环遍历list:

 for(int i=0;i<list.size();i++){
       if(list.get(i).equals("ls"))
       list.remove(i);
      }

这是一种很常见的遍历方式,但是使用这种遍历删除元素会出现问题,原因在于删除某个元素后,list的大小发生了变化,而你的索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第一个元素后,继续根据索引访问第二个元素后,因为删除的原因,后面的元素都往前移动了以为,所以实际访问的是第三个元素。因此,这种遍历方式可以用在读取元素,而不适合删除元素。

2、增强for循环:

    for(String x:list){
      if(x.equals("ls"))
      list.remove(x);
    }

这也是一种很常见的遍历方式,但是使用这种遍历删除元素也会出现问题,运行时会报ConcurrentModificationException异常
其实增强for循环是java语法糖的一种体现,如果大家通过反编译得到字节码,那么上面这段代码的内部实现如下所示:

for(Iterator<String> it = list.iterator();it.hasNext();){
     String s = it.next();
     if(s.equals("madehua")){
       list.remove(s);
     }
   }

下面就解释为什么会报ConcurrentModificationException异常。分析Iterator的源代码,重点分析整个调用该过程中的
函数(hasNext和remove):

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;
 
 
	    public boolean hasNext() {
	      return cursor != size; // size为集合中元素的个数
	    }
 
 
	    public E next() {
	      checkForComodification();
	      int i = cursor;
	      if (i >= size)
	        throw new NoSuchElementException();
	      Object[] elementData = ArrayList.this.elementData;
	      if (i >= elementData.length)
	        throw new ConcurrentModificationException();
	      cursor = i + 1;
	      return (E) elementData[lastRet = i];
	    }
 
 
	    /* 此方法并没被调用,只是调用List.remove方法
	    public void remove() {
	      checkForComodification();
	      try {
	        ArrayList.this.remove(lastRet);	// size字段减1
	        cursor = lastRet;
	        lastRet = -1;
	        expectedModCount = modCount;
	      } catch (IndexOutOfBoundsException ex) {
	        throw new ConcurrentModificationException();
	      }
	    }
	    */
 
 
	    final void checkForComodification() {	// 检查修改和当前版本号是否一致,不一致则抛出异常
	      if (modCount != expectedModCount)
	        throw new ConcurrentModificationException();
	    }
 
 
  	}
 
 
  	// List.remove
  	@Override public boolean remove(Object object) {
	    Object[] a = array;
	    int s = size;
	    if (object != null) {
	      for (int i = 0; i < s; i++) {
	        if (object.equals(a[i])) {
	          System.arraycopy(a, i + 1, a, i, --s - i);
	          a[s] = null; // Prevent memory leak
	          size = s;
	          modCount++;	// 核心代码:修改了版本号。这样当checkForComodification的时候,modCount值就和expectedModCount不同
	          return true;
	        }
	      }
	    } else {
	      for (int i = 0; i < s; i++) {
	        if (a[i] == null) {
	          System.arraycopy(a, i + 1, a, i, --s - i);
	          a[s] = null; // Prevent memory leak
	          size = s;
	          modCount++;
	          return true;
	        }
	      }
	    }
	    return false;
	  }

接下来梳理一下流程,这时候你就会发现这个异常是在next方法的checkForComodification中抛出的。抛出的原因是
modCount !=expectedModCount。这里的modCount是指这个list对象从呢我出来到现在被修改的次数,当调用list
的add或者remove方法的时候,这个modCount都会自动增减;iterator创建的时候modCount被复制给了
expectedModcount,但是调用list的add和remove方法的时候不会同时自动增减expectedModcount,这样就导致
两个count不相等,从而抛出异常。大家如果理解了上面的执行流程,以后碰到类似这种问题,比如如果删除的是倒数
第二个元素却不会碰到异常。就会知道为什么了。

3、iterator遍历删除

   Iterator<String> it = list.iterator();
   while(it.hasNext()){
    String x = it.next();
    if(x.equals("del")){
      it.remove();
   }
  }

这种方式是可以正常遍历和删除的。但是你可能看到上面代码感觉和增强for循环内部实现的代码差不多,其实差别就在于上面使用一个使用list.remove(),一个使用it.remove()。

(二)HashMap的遍历删除及如何实现遍历删除

一样我们先造一个hashmap出来,如下

	private static HashMap<Integer, String> map = new HashMap<Integer, String>();;
 
	public static void main(String[] args) {
		
		 for(int i = 0; i < 10; i++){ 
	      map.put(i, "value" + i); 
	    } 
	 
	
	}

1、第一种遍历删除:

    for(Map.Entry<Integer, String> entry : map.entrySet()){ 
       Integer key = entry.getKey(); 
       if(key % 2 == 0){ 
         System.out.println("To delete key " + key); 
         map.remove(key); 
         System.out.println("The key " + + key + " was deleted"); 
       } 

这种遍历删除依旧会报ConcurrentModificationException异常,

2、第二种遍历删除:

    Set<Integer> keySet = map.keySet();
	   for(Integer key : keySet){
	      if(key % 2 == 0){
	        System.out.println("To delete key " + key);
	        keySet.remove(key);
	        System.out.println("The key " + + key + " was deleted");
	      }
	   }

这种遍历删除依旧会报ConcurrentModificationException异常,

3、第三种遍历删除:

  Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
    while(it.hasNext()){
      Map.Entry<Integer, String> entry = it.next();
      Integer key = entry.getKey();
      if(key % 2 == 0){
      	 System.out.println("To delete key " + key);
      	 it.remove();  
      	 System.out.println("The key " + + key + " was deleted");
 
      }
    }

这种遍历是OK的

分析上述原因,如果大家理解了List的遍历删除,那么感觉HashMap的遍历删除是不是有类似之处啊。下面就分析一下原因:
如果查询源代码以上的三种的删除方式都是通过调用HashMap.removeEntryForKey方法来实现删除key的操作。
在removeEntryForKey方法内知识一场了key  modCount就会执行一次自增操作,此时modCount就与expectedModCOunt不一致了
,上面三种remove实现中,只有第三种iterator的remove方法在调用完removeEntryForKey方法后同步了expectedModCount值与
modCount相同,所以iterator方式不会抛出异常。最后希望大家遇到问题到查询源代码,它会给你最好的解释!
---------------------
作者:demohui
来源:CSDN
原文:https://blog.csdn.net/demohui/article/details/77748809
版权声明:本文为博主原创文章,转载请附上博文链接!

相关文章

  • Spring实战之使用Resource作为属性操作示例

    Spring实战之使用Resource作为属性操作示例

    这篇文章主要介绍了Spring实战之使用Resource作为属性,结合实例形式分析了spring载人Resource作为属性相关配置与使用技巧,需要的朋友可以参考下
    2020-01-01
  • Idea 同一窗口导入多个项目的实现步骤

    Idea 同一窗口导入多个项目的实现步骤

    本文主要介绍了Idea 同一窗口导入多个项目的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • java如何完成输出语句实例详解

    java如何完成输出语句实例详解

    输入输出可以说是计算机的基本功能,下面这篇文章主要给大家介绍了关于java如何完成输出语句的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • Java微服务Nacos Config配置中心超详细讲解

    Java微服务Nacos Config配置中心超详细讲解

    配置文件相对分散。在一个微服务架构下,配置文件会随着微服务的增多变的越来越多,而且分散 在各个微服务中,不好统一配置和管理。每一个环境所使用的配置理论上都是不同的,一旦需要修改,就需要我们去各个微服务下手动维护
    2023-02-02
  • java基于JDBC连接Oracle 11g Release2实例分析

    java基于JDBC连接Oracle 11g Release2实例分析

    这篇文章主要介绍了java基于JDBC连接Oracle 11g Release2的方法,实例分析了JDBC连接Oracle 11g Release2容易出现的异常与解决方法,需要的朋友可以参考下
    2015-06-06
  • java 查询oracle数据库所有表DatabaseMetaData的用法(详解)

    java 查询oracle数据库所有表DatabaseMetaData的用法(详解)

    下面小编就为大家带来一篇java 查询oracle数据库所有表DatabaseMetaData的用法(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • java中JDBC实现往MySQL插入百万级数据的实例代码

    java中JDBC实现往MySQL插入百万级数据的实例代码

    这篇文章主要介绍了java中JDBC实现往MySQL插入百万级数据的实例代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-01-01
  • jstl之map,list访问遍历以及el表达式map取值的实现

    jstl之map,list访问遍历以及el表达式map取值的实现

    下面小编就为大家带来一篇jstl之map,list访问遍历以及el表达式map取值的实现。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • java解析sina视频

    java解析sina视频

    本文介绍了一个java解析sina视频地址的例子,从这个例子中可以学习到java使用sax解析xml的方法,大家可以参考修改成其它功能
    2014-01-01
  • mybatis-generator-gui 工具使用(图形化工具)

    mybatis-generator-gui 工具使用(图形化工具)

    基于 mybatis generator 开发一款界面工具, 本工具可以使你非常容易及快速生成 Mybatis 的 Java POJO 文件及数据库 Mapping 文件。本文重点给大家介绍mybatis-generator-gui 工具使用,感兴趣的朋友一起看看吧
    2022-03-03

最新评论