1.ArrayList如何去重?
hashSet(不保证元素顺序) LinkedHashSet(顺序一致)
2.集合遍历当中如何删除指定元素?
1. 通过迭代器删除(实际项目中推荐)
while (iterator.hasNext()) {
Student student = iterator.next();
if ("male".equals(student.getGender())) {
iterator.remove();
//list.remove(male),在删除【除开倒数第二个】元素时会产生ConcurrentModificationException异常
}
}
为什么不通过迭代器自带的方法就会ConcurrentModificationException异常? 下面要介绍一下迭代器:
- 迭代器介绍
当使用Iterator迭代访问Collection集合元素时,Collection集合里的元素不能被改变,只有通过Iterator的remove()方法删除上一次next()方法返回的集合元素才可以;否则会引发java.util.ConcurrentModificationException异常。
之所以会出现这样的异常,是因为Iterator迭代器采用的是快速失败(fast-fail)机制,一旦在迭代过程中检测到该集合已经被修改(通常是程序中其它线程修改),程序立即引发ConcurrentModificationException,
迭代器是如何知道被修改了呢?为何删除倒二元素不会异常?
首先看一下迭代器的源码:
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; //迭代器初始化时候设置的,一旦容器结构发生变化,会改变modCount的值,进而引发后面的异常
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
...
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
可以清楚的看到迭代器里维护了cursor,expectedModCount两个字段。modCount则由list维护。 一旦容器结构发生变化,modCount的值会发生变化,每次累加1,modCount相当于一个记录ArrayList版本的变量;expectedModCount在生成迭代器时候进行初始化,代表初始化时候容器的modCount。
每次next()方法执行完之后,cursor都代表当前元素的下一个元素的下标。 在hasNext()方法中,是将光标与size对比,删除倒二后cursor == size,故没有异常;
为何使用迭代器删除不会异常?
因为在迭代器的remove()方法里,expectedModCount = modCount,重置了expectedModCount;
2. 在高级for循环中,直接list.remove(int i);
for (int a:list) {
if(a == 1){
list.remove(a);
//break;
}
}
如果在list.remove(a)后没有break,而是继续遍历,则会报错java.util.ConcurrentModificationException
3. 普通for循环,直接list.remove(int i);
list.remove()只是删除元素,不会改变原有元素的位置,举例:remove(nums[1]),指向原nums[2]的值,i++后指向nums[3]