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

网友投稿 447 2023-08-06

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

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

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

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

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

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

List list= new ArrayList();

famous.add("zs");

famous.add("ls");

famous.add("ww");

famous.add("dz");

1、for循环遍历list:

for(int i=0;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(http://Iterator 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 {

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 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 map = new HashMap();;

public static void main(String[] args) {

for(int i = 0; i < 10; i++){

map.put(i, "value" + i);

}

}

1、第一种遍历删除:

for(Map.Entry 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 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> it = map.entrySet().iterator();

while(it.hasNext()){

Map.Entry entry = it.next();

Integer key = entry.getKey();

if(key % 2 == 0){

System.out.println("To delete key " + key);

it.remove();

System.out.printlCZDFwrjAn("The key " + + key + " was deleted");

}

}

这种遍历是OK的

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(http://Iterator 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 {

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 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 map = new HashMap();;

public static void main(String[] args) {

for(int i = 0; i < 10; i++){

map.put(i, "value" + i);

}

}

1、第一种遍历删除:

for(Map.Entry 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 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> it = map.entrySet().iterator();

while(it.hasNext()){

Map.Entry entry = it.next();

Integer key = entry.getKey();

if(key % 2 == 0){

System.out.println("To delete key " + key);

it.remove();

System.out.printlCZDFwrjAn("The key " + + key + " was deleted");

}

}

这种遍历是OK的

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:学习非阻塞的同步机制CAS
下一篇:Springboot项目平滑关闭及自动化关闭脚本
相关文章

 发表评论

暂时没有评论,来抢沙发吧~