`

Java中如何删除一个集合中的多个元素(1)

    博客分类:
  • Java
阅读更多
今天我需要从一个java的集合中,根据另一个集合的内容,删除第一个集合中不特定的元素。这看上去非常简单,但却遇到了问题。这就是“Java中如何删除一个集合中的多个元素”的问题。
这是我要写的方法的头部
private void screenBlackNameList(List<SharedBoardSmsWrapper> source, List<BlackNameListModel> blackNameList)

事情是这样子的。source集合中保存了一些显示用的数据元素。blackNameList集合中保存的是黑名单列表。我们需要根据黑名单表,把source集合中黑名单用户的数据剔除掉。

这个问题的解决看上去非常简单。

我首先使用for each 语句进行删除。
for(SharedBoardSmsWrapper tmpSharedBoardSmsWrapper:source){
        
        for(BlackNameListModel tmpBlackNameListModel:blackNameList){
            if(tmpSharedBoardSmsWrapper.getSource().equals(tmpBlackNameListModel.getSource())){
               source.remove(tmpSharedBoardSmsWrapper);
               break;
            }
            
        }
    }

非常简单的问题!我暗笑,
测试…
令我意外的是,这段代码居然抛出了异常
java.util.ConcurrentModificationException。
查看JDK6手册

public class ConcurrentModificationException
extends RuntimeException
当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
例如,某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该 Collection。通常在这些情况下,迭代的结果是不确定的。如果检测到这种行为,一些迭代器实现(包括 JRE 提供的所有通用 collection 实现)可能选择抛出此异常。执行该操作的迭代器称为快速失败 迭代器,因为迭代器很快就完全失败,而不会冒着在将来某个时间任意发生不确定行为的风险。
注意,此异常不会始终指出对象已经由不同 线程并发修改。如果单线程发出违反对象协定的方法调用序列,则该对象可能抛出此异常。例如,如果线程使用快速失败迭代器在 collection 上迭代时直接修改该 collection,则迭代器将抛出此异常。
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败操作会尽最大努力抛出 ConcurrentModificationException。因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException 应该仅用于检测 bug。

   Java中的For each实际上使用的是iterator进行处理的。而iterator是不允许集合在iterator使用期间删除的。而我在for each时,从集合中删除了一个元素,这导致了iterator抛出了ConcurrentModificationException。

看来只有老老实实使用传统的for循环了!
for(int i=0;i<source.size();i++){
        SharedBoardSmsWrapper tmpSharedBoardSmsWrapper=source.get(i);
        for(int j=0;j<blackNameList.size();j++){
            BlackNameListModel tmpBlackNameListModel=blackNameList.get(j);
            if(tmpSharedBoardSmsWrapper.getSource().equals(tmpBlackNameListModel.getSource())){
               source.remove(tmpSharedBoardSmsWrapper);
               break;
            }
        }
    }

这下应该没问题了吧!信心满满地按下测试…
晕!怎么回事,数据怎么过滤得不对?

Debug跟踪后发现,原来,集合删除元素时,集合的size会变小,连带索引都会改变!
这可怎么办?我不会被这样一个小问题搞得没辙了吧!

使用Iterator删除集合中的元素

查看JDK手册的Iterator接口,看到它还有一个remove方法。
remove
void remove()
从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。每次调用 next 只能调用一次此方法。如果进行迭代时用调用此方法之外的其他方式修改了该迭代器所指向的 collection,则迭代器的行为是不确定的。
抛出:
UnsupportedOperationException - 如果迭代器不支持 remove 操作。
IllegalStateException - 如果尚未调用 next 方法,或者在上一次调用 next 方法之后已经调用了 remove 方法。
/**
     *@paramsource
     *@paramblackNameList
     */
    privatevoid screenBlackNameList(List<SharedBoardSmsWrapper> source, List<BlackNameListModel> blackNameList){
    Iterator<SharedBoardSmsWrapper> sourceIt=source.iterator();
    
    while(sourceIt.hasNext()){
        SharedBoardSmsWrapper tmpSharedBoardSmsWrapper=sourceIt.next();
        Iterator<BlackNameListModel> blackNameListIt=blackNameList.iterator();
        while(blackNameListIt.hasNext()){
            BlackNameListModel tmpBlackNameListModel=blackNameListIt.next();
           if(tmpSharedBoardSmsWrapper.getSource().equals(tmpBlackNameListModel.getSource())){
               sourceIt.remove();
               break;
            }
        }
    }
    }

注意,一次Iterator的next()方法,不能多次调用remove()方法。否则会抛出异常。

看来,删除集合中的元素,最简单的方法,就是使用Iterator的remove()方法了!
让我们看看ArrayList类提供的Iterator是怎样实现的。
privateclass Itr implements Iterator<E> {
    /**
这是元素的索引,相当于一个指针,或者游标,利用它来访问List的数据元素。
     *Indexofelementtobereturnedbysubsequentcalltonext.
     */
    intcursor = 0;

    /**
     *Indexofelementreturnedbymostrecentcalltonextor
     *previous. Resetto-1ifthiselementisdeletedbyacall
     *toremove.
最新元素的索引。如果已经删除了该元素,就设为-1
     */
    intlastRet = -1;

    /**
外部类ArrayList的属性:
protected transient int modCount = 0;
    它用于观察ArrayList是否同时在被其他线程修改,如果不一致,那么就会抛出同步异常。
     *ThemodCountvaluethattheiteratorbelievesthatthebacking
     *Listshouldhave. Ifthisexpectationisviolated,theiterator
     *hasdetectedconcurrentmodification.
     */
    intexpectedModCount = modCount;
//如果游标没有达到List的尺寸,那么就还有元素。
    publicboolean hasNext() {
            returncursor != size();
    }
//返回当前元素,然后游标+1。最近索引 也= 返回的元素的索引。
    public E next() {
            checkForComodification();
        try {
       E next = get(cursor);
       lastRet = cursor++;
       return next;
        } catch (IndexOutOfBoundsException e) {
       checkForComodification();
       thrownew NoSuchElementException();
        }
    }
/*
删除元素,就是删除当前元素,并且把游标-1。因为,List会把后面的元素全部移前一位。
*/
    publicvoid remove() {
        if (lastRet == -1)
       thrownew IllegalStateException();
            checkForComodification();

        try {
       AbstractList.this.remove(lastRet);
       if (lastRet < cursor)
           cursor--;
       lastRet = -1;
       expectedModCount = modCount;
        } catch (IndexOutOfBoundsException e) {
       thrownew ConcurrentModificationException();
        }
    }

    finalvoid checkForComodification() {
        if (modCount != expectedModCount)
       thrownew ConcurrentModificationException();
    }
    }

可以看到,Iterator删除了元素,并且把游标重新置为正确的位子。只要没有其他线程同时改变该集合,就不会有任何问题。
分享到:
评论

相关推荐

    java集合知识-map、set等

    记住:如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。 一般情况下,如果自定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals,hashCode方法。 建立对象判断是否相同的依据。...

    实现Java删除一个集合的多个元素

    Java中的For each实际上使用的是iterator进行处理的。而iterator是不允许集合在...而我在for each时,从集合中删除了一个元素,这导致了iterator抛出了ConcurrentModificationException,下面来看看到底怎么回事。

    Collique:一个只能包含一个元素的java集合库

    科利克收藏独特一个库,提供只能包含一个元素的主要 Java 集合(List、Set、Map)的实现。为什么 ? Java API 已经为空集合提供了存根(在java.util.collections可用),但没有为单个元素提供存根。 问题是:为什么...

    Java集合框架.ppt

    集合是将多个元素组成一个单元的对象; 类似于数组,但数组最大的缺点是:长度受到限制(一经创建,就不可再改变),并且只能存放相同数据类型的元素; 集合的长度没有限制,可以存放任意多的元素,而且元素的数据...

    java集合类

    对象数组中包含一组对象,但是对象数组使用的时候存在一个长度的限制,那么集合是专门解决这种限制的,使用集合可以方便的向数组中增加任意多个数据。 集合类使用初始容量和加载因子调整自己的大小。集合类全部支持...

    java实现把一个List集合拆分成多个的操作

    主要介绍了java实现把一个List集合拆分成多个的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

    Java集合框架.pdf

    Java集合框架是一个抽象数据类型的框架,它提供了一组接口和类,可用于处理各种类型的数据结构,如列表、队列、集、映射等。 Java集合框架的主要特点是: 1、可扩展性:Java集合框架提供了一组可扩展的接口和类,可...

    Java判断2个List集合是否相等(不考虑元素的顺序)

    今天小编就为大家分享一篇关于Java判断2个List集合是否相等(不考虑元素的顺序)的文章,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

    java集合-HashSet的使用

    HashSet 是 Java 中的一个集合类,它实现了 Set 接口并提供了基于哈希表的无序、不重复元素的集合。具体来说,它是通过哈希表(实际上是一个 HashMap 实例)来存储元素的。 以下是 HashSet 的一些主要特点: 无序...

    Java综合实验---学生信息管理系统

    3) 将多个对象存放在一个适当的集合中,集合中要使用泛型; 4) 使用合适的方法对集合中的数据按一定的方式排序; 5) 使用迭代器遍历集合并输出集合的元素,将排序后输出的结果写入out.txt文件中,IO操作需要有异常...

    JAVA_API1.6文档(中文)

    java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...

    Java集合框架List接口.pdf

    Java集合框架中的List接口是一种有序的集合,它可以存储重复的元素。它是Collection接口的子接口,提供了一系列可以对列表进行操作的方法,如添加、插入、删除、获取元素等。List接口还可以通过索引访问元素,类似于...

    Java 集合方面的面试题

    如何使用 Java 8 中的新特性 Optional 类型来处理可能为 null 的集合元素? 如何使用 ConcurrentHashMap 类来实现高效的并发缓存? 如何使用 Spliterator 接口来实现自定义的集合遍历逻辑? 如何使用

    java8 利用reduce实现将列表中的多个元素的属性求和并返回操作

    主要介绍了java8 利用reduce实现将列表中的多个元素的属性求和并返回操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

    java中map集合思维导图.xmind

    集合就是用存储多个元素的,动态的扩张长度,弥补了数组固定大小的缺陷,运用思维导图来展示不同类型集合之间的关系和区别。

    java集合-CopyOnWriteArraySet的使用

    CopyOnWriteArraySet 是Java中的一个线程安全的集合类,它实现了 Set 接口并使用了"写时复制"的机制。 下面是关于 CopyOnWriteArraySet 的一些重要信息: 线程安全性:CopyOnWriteArraySet 是线程安全的,可以在多...

    Java集合教程之Collection实例详解

    集合,或者叫容器,是一个包含多个元素的对象,下面这篇文章主要给大家介绍了关于Java集合教程之Collection的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下

    JavaAPI1.6中文chm文档 part1

    java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...

    java集合-ConcurrentSkipListSet的使用

    ConcurrentSkipListSet 是Java中的一个线程安全的有序集合类,它基于跳表(Skip List)数据结构实现。 下面是关于 ConcurrentSkipListSet 的一些重要信息: 线程安全性:ConcurrentSkipListSet 是线程安全的,可以...

    利用Java手写一个布隆过滤器Bloom Filter

    布隆过滤器是一种数据结构,主要用于判断一个元素是否可能在一个集合中存在。它可以在插入和查询数据时快速地判断一个元素是否可能在这个集合中,比如在缓存中查询一个元素是否存在。 它的原理是使用多个哈希函数对...

Global site tag (gtag.js) - Google Analytics