ArrayList删除元素的方式。
- 使用for循环删除。会遗漏删除的元素
- 使用foreach删除。删除多个会抛ConcurrentModificationException异常。
测试用例
public class RemoveCollection {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hel");
list.add("peo");
list.add("she");
list.add("he");
list.add("he");
list.add("haha");
forRemove(list,"he"); //结果[hel, peo, she, he, haha]
// forEachRemove(list,"he");//抛出异常
// System.out.println(RemoveCollectionUtils.removeList(list, "he", "hel"));
System.out.println(list);
}
public static <T> void forRemove(List<T> list, T obj) {
for (int i = 0; i < list.size(); i++) {
if(list.get(i).equals(obj)){
list.remove(obj);
}
}
}
public static <T> void forEachRemove(List<T> list, T obj) {
for (T element:list) {
if(element.equals(obj)){
list.remove(obj);
}
}
}
}
使用for删除遗漏元素的原因:
当删除的时候size值改变,list中删除元素的后面元素会向前移。导致下次遍历的时候错过元素。从后面开始遍历,则不会遗漏元素。比如把forRemove改成如下代码。
public static <T> void forRemove(List<T> list, T obj) {
for (int i = list.size()-1; i >= 0; i--) {
if(list.get(i).equals(obj)){
list.remove(obj);
}
}
}
总结:
使用for循环删除时,逆序比较删除即可。
使用foreach删除报错的原因:
实现Iterable接口的类,均可使用foreach这种语法。实质上会调用实现类的iterator方法,接着调用hasNext()、next()方法。ArrayList中的继承关系图:
ArrayList中iterator方法。
public Iterator<E> iterator() {
return new Itr();
}
AbstractList中有一个modCount变量。在new Itr 时modCount值赋给expectedModCount。调用next方法时会校验modCount与expectedModCount值,不相同抛出异常。Itr()类的代码
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;
}
@SuppressWarnings("unchecked")
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ArrayList中的remove方法。每次remove的时候modCount就会加一。add方法的也会modCount就会加一。导致remove之后,下次遍历的时候,即调用next()方法的时候,抛出异常。
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
解决办法:
①若确定list中只有一个元素,那么找到此元素之后进行break;
②有多个则 Iterator iterator = list.iterator();可能会有疑问,foreach的时候调用的就是iterator方法啊,为什么这种可以?使用foreach的时候调用的remove方法是ArrayList中的。使用list.iterator()进行删除时调用的是Itr类中的remove方法。
其它集合的删除方法
线程不安全的类都有modCount变量。如以下类:
- ArrayList
- LinkedList
- HashMap
- LinkedHashMap
- TreeMap
- HashSet
- LinkedHashSet
- TreeSet
以上类中在删除元素时注意,因Map的key与Set的元素不能重复顾可使用foreach break的方式。其余参考Arraylist删除时的解决办法。
总结:
以上类在执行add跟remove方法时都会改变modCount的值。
删除集合元素的工具类
可以根据元素内容进行删除。支持可变参数。比较时调用的是集合元素中的equals方法。
public abstract class RemoveCollectionUtils {
public static <T> List<T> removeList(List<T> list, T obj) {
//以空间换时间,不用扩容
List<T> removeList = new ArrayList<>(list.size());
Iterator<T> iterator = list.iterator();
while (iterator.hasNext()) {
//一次hasNext只能一次
T t = iterator.next();
if (t.equals(obj)) {
removeList.add(t);
iterator.remove();
}
}
return removeList;
}
public static <T> List<T> removeList(List<T> list, T... objArray) {
//以空间换时间,不用扩容
List<T> removeList = new ArrayList<>(list.size());
for (T obj : objArray) {
removeList.addAll(removeList(list, obj));
}
return removeList;
}
public static <K, V> K removeMapByKey(Map<K, V> map, K key) {
//因为key在Map中只有一个
Iterator<Map.Entry<K, V>> itMap = map.entrySet().iterator();
while (itMap.hasNext()) {
K removeKey = itMap.next().getKey();
if (removeKey.equals(key)) {
itMap.remove();
return removeKey;
}
}
return null;
}
public static <K, V> List<V> removeMapByValue(Map<K, V> map, V value) {
//因为key在Map中只有一个
List<V> removeListValue = new ArrayList<>(map.size());
Iterator<Map.Entry<K, V>> itMap = map.entrySet().iterator();
while (itMap.hasNext()) {
V removeValue = itMap.next().getValue();
if (removeValue.equals(value)) {
itMap.remove();
removeListValue.add(removeValue);
return removeListValue;
}
}
return removeListValue;
}
public static <K, V> List<V> removeMapByValue(Map<K, V> map, V... valueArray) {
//因为key在Map中只有一个
List<V> removeListValue = new ArrayList<>(map.size());
for (V obj : valueArray) {
removeListValue.addAll(removeMapByValue(map, obj));
}
return removeListValue;
}
public static <K> K removeSet(Set<K> set, K key) {
//因为key在Set中只有一个
Iterator<K> iteratorKey = set.iterator();
while (iteratorKey.hasNext()) {
K removeKey = iteratorKey.next();
if (removeKey.equals(key)) {
iteratorKey.remove();
return removeKey;
}
}
//没有找到返回null
return null;
}
}
每篇一语:
“你的醒来,使我欢喜。我正在想着走出冰谷的方法;我愿意携带你去,使你永不冰结,永得燃烧。”
“唉唉!那么,我将烧完!”
“你的烧完,使我惋惜。我便将你留下,仍在这里罢。”
“唉唉!那么,我将冻灭了!”
——《死火》 鲁迅