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

相信大家对集合遍历再熟悉不过了,这里总结一下HashMap和List的遍历方法,以及它们该如何实现遍历删除。这里对于每种遍历删除出现的问题的原因都给出了详解!

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

我们造一个list出来,接下来用不同方法遍历删除,如下代码:
    List<String> list= new ArrayList<String>();
    famous.add("zs");
    famous.add("ls");
    famous.add("ww");
    famous.add("dz");

for循环遍历list:

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

增强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不相等,从而抛出异常。 大家如果理解了上面的执行流程,以后碰到类似这种问题,比如如果删除的是倒数 第二个元素却不会碰到异常。就会知道为什么了。
 

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

第一种遍历删除:

        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异常,
 

第二种遍历删除:

       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异常,
 

第三种遍历删除:

   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方式不会抛出异常。 最后希望大家遇到问题到查询源代码,它会给你最好的解释!
  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值