• 技术文章 >Java >java教程

    Java Collection Framework -Collection接口的具体详解

    黄舟黄舟2017-03-14 10:27:08原创1933


    一、要点


    二、Collection 接口

    1、JDK 对 Collection 接口的描述

      Collection 接口是 Collection 层次结构中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection(List,Queue) 允许有重复的元素,而另一些(Set)则不允许。一些 collection (List,Queue)是有序的,而另一些(Set)则是无序的。JDK 不提供此接口的任何直接实现:它提供更具体的子接口(如 Set 和 List)实现,这些子接口继承自Collection 接口。此接口通常用来传递 collection(多态),并在需要最大普遍性的地方操作这些 collection

      所有通用的 Collection 实现类(通常通过它的一个子接口间接实现 Collection)应该提供 两个“标准”构造方法一个是 void(无参数)构造方法,用于创建空 collection;另一个是带有 Collection 类型单参数的构造方法,用于创建一个具有与其参数相同元素新的 collection。实际上,后者允许用户复制任何 collection,以生成所需实现类型的一个等效 collection。尽管无法强制执行此约定(因为接口不能包含构造方法),但是 Java 平台库中所有通用的 Collection 实现都遵从它。

      此接口中包含的“破坏性”方法,是指可修改其所操作的 collection 的那些方法,如果此 collection 不支持该操作,则指定这些方法抛出 UnsupportedOperationException(可选操作)。

      Collections Framework 接口中的很多方法是根据 equals 方法定义的。

      Collections Framework 中的各个容器均是非线程安全的。
      
      Collection 接口的定义如下:

    public interface Collection<E> extends Iterable<E> { ... }

                Java Collection Framework.png-155.6kB

    php入门到就业线上直播课:进入学习


    2、行为

       java.util.Collection 接口是描述 Set 和 List 集合类型的根接口,下面给出了Collection 的所有行为(不包括从Object 继承而来的方法),因此它们也刻画了 Set 或 List 执行的所有行为(List 还有额外的功能)。Collection 的行为分类和详细列表如下 :

         Collection行为分类.png-29.5kB
          
                            表1. Collection 的操作

    FunctionIntroductionNote
    boolean add(T)将指定元素添加到集合中,若未将该元素添加到容器,返回 false可选操作
    boolean addAll(Collection<? extends T>)添加参数中的所有元素,只要添加进任意元素则返回 true可选操作
    Iterator iterator( )遍历容器中的元素(统一的访问、遍历方法,对外屏蔽了不同容器的差异性,迭代器模式依赖于具体实现
    boolean remove(Object)向容器移除指定元素,若移除动作发生,则返回 true(若有重复对象,一次只删除其中一个可选操作
    boolean removeAll(Collection<?>)向容器移除参数中所有元素,若有移除动作发生,则返回 true可选操作
    Boolean retainAll(Collection<?>)只保存参数中的元素,只要Collection 发生改变,则返回 true可选操作
    void clear()移除容器中所有元素可选操作
    boolean contains(T)若容器持有该元素,返回 true依赖于equals()
    Boolean containsAll(Collection<?>)若容器持有参数中的所有元素,返回 true依赖于equals()
    object[] toArray()返回一个包含该容器所有元素的 Object 型数组Array与Collection之间的桥梁,在 AbstractCollection 中提供默认实现,子类可以对它重写
    T[] toArray(T[] a)返回一个包含该容器所有元素的 Object 型数组,且返回结果的运行时类型与参数数组 a 相同,而不单纯是objectArray与Collection之间的桥梁,在 AbstractCollection 中提供默认实现,子类可以对它重写
    int size()返回容器中元素数目依赖于具体实现
    boolean isEmpty()容器为空,则返回 true依赖于size()

       注意到,其中不包含随机访问所选择的元素 get() 方法,因为 Collection 包含 Set,而 Set 是自己维护顺序的。因此,若想访问、遍历 Collection 中的元素,则必须使用迭代器; 另外,这些方法的默认实现(AbstractCollection)均直接或间接使用了迭代器。


    三、Collection 接口的默认实现:AbstractCollection

    1、JDK 对 AbstractCollection 抽象类的描述

       此抽象类提供 Collection 接口的骨干实现,以最大限度地减少实现此接口所需的工作。 要实现一个不可修改的 collection,编程人员只需扩展此类,并提供 iterator 和 size 方法的实现。(iterator 方法返回的迭代器必须实现 hasNext 和 next。); 要实现可修改的 collection,编程人员必须另外重写此类的 add 方法(否则,会抛出 UnsupportedOperationException),iterator 方法返回的迭代器还必须另外实现其 remove 方法(AbstractCollection 的移除方法间接调用迭代器的移除方法)。按照 Collection 接口规范中的建议,编程人员通常应提供一个 void (无参数)和 Collection 构造方法。

        Iterator.png-6.6kB

      AbstractCollection 抽象类定义如下:

    public abstract class AbstractCollection<E> implements Collection<E> { ... }

    2、行为(对上述 Collection 接口中方法的实现情况)

      下面给出了抽象类 AbstractCollection 对 Collection 接口的实现情况:

        public boolean add(E e) {   
         throw new UnsupportedOperationException();
        }  
         public boolean addAll(Collection<? extends E> c) {  
          boolean modified = false;
        Iterator<? extends E> e = c.iterator();   
         while (e.hasNext()) {     
           if (add(e.next()))                
             // 调用了上面定义的 add 方法
            modified = true;
        }    return modified;
        }    public abstract Iterator<E> iterator();               
        //未提供具体实现,将实现延迟到具体容器
    
    
        public boolean remove(Object o) {
        Iterator<E> e = iterator();    
        if (o==null) {       
         while (e.hasNext()) {       
          if (e.next()==null) {
                e.remove();                 
                //内部使用了迭代器的 remove 方法,故具体容器的迭代器实现密切相关
                return true;
            }
            }
        } else {        
        while (e.hasNext()) {      
          if (o.equals(e.next())) {
                e.remove();           
                 return true;
            }
            }
        }    return false;
        }    public boolean removeAll(Collection<?> c) {  
          boolean modified = false;
        Iterator<?> e = iterator();    
        while (e.hasNext()) {     
           if (c.contains(e.next())) {
            e.remove();                       
            //内部使用了迭代器的 remove 方法,故具体容器的迭代器实现密切相关
            modified = true;
            }
        }    return modified;
        }    public boolean retainAll(Collection<?> c) {    
        boolean modified = false;
        Iterator<E> e = iterator();   
         while (e.hasNext()) {        
        if (!c.contains(e.next())) {
            e.remove();                       
            //内部使用了迭代器的 remove 方法,故具体容器的迭代器实现密切相关
            modified = true;
            }
        }    return modified;
        }    public void clear() {
        Iterator<E> e = iterator();    
        while (e.hasNext()) {
            e.next();
            e.remove();                       
            //内部使用了迭代器的 remove 方法,故具体容器的迭代器实现密切相关
        }
        }    public boolean contains(Object o) {
        Iterator<E> e = iterator();    
        if (o==null) {        
        while (e.hasNext())        
        if (e.next()==null)            
        return true;
        } else {        while (e.hasNext())        
        if (o.equals(e.next()))                 
        //内部调用了的 equals方法,故与 equals 方法实现密切相关
                return true;
        }    return false;
        }    public boolean containsAll(Collection<?> c) {
        Iterator<?> e = c.iterator();    
        while (e.hasNext())        
        if (!contains(e.next()))                
        //内部调用了的 contains 方法,故与 contains 方法实现密切相关
            return false;    return true;
        }    //此处省略两个 toArray 方法的实现
    
    
        public abstract int size();                   
        //未提供具体实现,将实现延迟到具体容器
    
    
        public boolean isEmpty() {    return size() == 0;                      
        //内部调用了的 size 方法,故与 size 方法实现密切相关
        }

       对以上实现进行总结 :


       从源代码中我们可以知道,几乎所有方法的实现都与迭代器相关,并且有以下特点:


    四、Optional operations(可选操作)

    1、简述

       执行各种不同的添加和移除方法在 Collection 接口中都是可选操作, 这意味着实现类并不需要为这些方法提供功能定义。

       这是一种很不寻常的接口定义方式。正如你所看到的那样,接口是面向对象设计中的契约,它声明 “无论你选择如何实现该接口,我保证你可以向该接口发送这些消息。”但可选操作违反这个非常基本的原则,它声明调用某些方法将不会执行有意义的行为,相反,他们会抛出异常

       为什么会将方法定义为可选呢? 因为这样做可以防止在设计中出现接口爆炸的情形。容器类库中的设计看起来总是为了描述每个主题的变体,而最终患上了令人困惑的接口过剩症。甚至这么做仍不能捕获接口的各种特例,因为总是有人会发明新的接口。“未获支持的操作” 这种方式可以实现 Java 容器类库的一个重要目标:容器应易学易用。未获支持的操作是一种特例,可以延迟到需要时实现。但是,为了让这种方式能够工作:

    1. UnsupportedOperationException 必须是一种罕见事件。即,对于大多数类而言,所有操作都应该可以工作,只有在特例(例如,通过Arrays.asList()所得到的容器)中才会有未获支持的操作。在 Java 容器类库中确实如此,因为你在 99% 的时间里使用的容器类,如 ArrayList、LinkedList、HashSet 和 HashMap,以及其他的具体实现,都支持所有操作。这种设计留下了一个“后门”,如果你想创建新的 Collection, 但是没有为 Collection 接口中的所有方法都提供有意义的定义,那么它仍旧适合现有类库。

    2. 如果一个操作是未获支持的,那么在实现接口的时候可能就会导致 UnsupportedOperationException 异常,而不是将产品程序交给客户后才出现此异常,这种情况是有道理的。毕竟,他表示编程上有错误:使用了不正确的接口实现。

       值得注意的是,未获支持操作只有在运行时才能探测到,因此他们表示动态类型检查。


    2、示例

       最常见的未获支持的操作,都来源于背后由固定尺寸的数据结构支持的容器。当你用 Arrays.asList() 将数组转换为 List 时,就会得到这样的容器。此外,你还可以通过使用 Collection 类中“不可修改”的方法,选择创建任何会抛出会抛出 UnsupportedOperationException 的容器。

       请看下面的例子:

    public class Unsupported {
        static void test(String msg, List<String> list) {
            System.out.println("--- " + msg + " ---");
            Collection<String> c = list;
            Collection<String> subList = list.subList(1, 8);        
            // Copy of the sublist:
            Collection<String> c2 = new ArrayList<String>(subList);    
                
            try {
                c.retainAll(c2);
            } catch (Exception e) {
                System.out.println("retainAll(): " + e);
            }        try {
                c.removeAll(c2);
            } catch (Exception e) {
                System.out.println("removeAll(): " + e);
            }        try {
                c.clear();
            } catch (Exception e) {
                System.out.println("clear(): " + e);
            }        try {
                c.add("X");
            } catch (Exception e) {
                System.out.println("add(): " + e);
            }        try {
                c.addAll(c2);
            } catch (Exception e) {
                System.out.println("addAll(): " + e);
            }        try {
                c.remove("C");
            } catch (Exception e) {
                System.out.println("remove(): " + e);
            }        // The List.set() method modifies the value but
            // doesn’t change the size of the data structure:
            try {
                list.set(0, "X");
            } catch (Exception e) {
                System.out.println("List.set(): " + e);
            }
        }    public static void main(String[] args) {
            List<String> list = Arrays.asList("A B C D E F G H I J K L".split(" "));
    
            test("Modifiable Copy", new ArrayList<String>(list)); // 产生新的尺寸可调的
                                                                    
                                                                    
                                                                   // ArrayList
    
            test("Arrays.asList()", list); // 产生固定尺寸的 ArrayList
    
            test("unmodifiableList()",
                    Collections.unmodifiableList(new ArrayList<String>(list))); // 产生不可修改的列表
        }
    } /* Output: 
    --- Modifiable Copy --- 
    
    --- Arrays.asList() --- 
    retainAll(): java.lang.UnsupportedOperationException 
    removeAll(): java.lang.UnsupportedOperationException 
    clear(): java.lang.UnsupportedOperationException 
    add(): java.lang.UnsupportedOperationException 
    addAll(): java.lang.UnsupportedOperationException 
    remove(): java.lang.UnsupportedOperationException 
    
    --- unmodifiableList() --- 
    retainAll(): java.lang.UnsupportedOperationException 
    removeAll(): java.lang.UnsupportedOperationException 
    clear(): java.lang.UnsupportedOperationException 
    add(): java.lang.UnsupportedOperationException 
    addAll(): java.lang.UnsupportedOperationException 
    remove(): java.lang.UnsupportedOperationException 
    List.set(): java.lang.UnsupportedOperationException

       因为 Arrays.asList() 实际上会产生一个 Arraylist ,它基于一个固定大小的数组,仅支持那些不会改变数组大小的操作。所以,任何会引起对底层数据结构的尺寸进行修改的方法(add/remove 相关)都会产生一个 UnsupportedOperationException 异常,以表示对该容器未获支持操作的调用。

       因此,Arrays.asList() 的真正意义在于:将其结果作为构造器参数传递给任何 Collection (或者使用 addAll 方法、Collections.addAll 静态方法),这样可以生成一个动态的容器

       由以上程序片段可知, Arrays.asList() 返回固定尺寸的List,而 Collections.unmodifiableList() 产生不可修改的列表。正如输出所示,前者支持 set 操作,而后者不支持。若使用接口,那么还需要两个附加的接口,一个具有可以工作的 set 方法,另一个没有,因为附加的接口对于 Collection 的各种不可修改子类型来说是必须的。因此,可选方法可以避免 接口爆炸

       针对 Arrays.asList() 这种情况给出解释:

    1.首先该方法的源码为:

      public static <T> List<T> asList(T... a) {    return new ArrayList<T>(a);
        }

    2.紧接着看上述方法所返回的由固定尺寸的数据结构支持的容器源码(部分):

    private static class ArrayList<E> extends AbstractList<E>    
    implements RandomAccess, java.io.Serializable
        {
        private static final long serialVersionUID = -2764017481108945198L;   
         private final E[] a;
    
        ArrayList(E[] array) {            
        if (array==null)                
        throw new NullPointerException();
            a = array;
        }    public int size() {       
         return a.length;
        }    public Object[] toArray() {       
         return a.clone();
        }    public <T> T[] toArray(T[] a) {       
         int size = size();        
        if (a.length < size)        
        return Arrays.copyOf(this.a, size,
                         
                         (Class<? extends T[]>) a.getClass());
            System.arraycopy(this.a, 0, a, 0, size);        
            if (a.length > size)
            a[size] = null;        
            return a;
        }    public E get(int index) {        
        return a[index];
        }    public E set(int index, E element) {        
        //该容器支持的操作
            E oldValue = a[index];
            a[index] = element;        
            return oldValue;
        }    public int indexOf(Object o) {        
        if (o==null) {            
        for (int i=0; i<a.length; i++)                
        if (a[i]==null)                    
        return i;
                    } else {                    
                    for (int i=0; i<a.length; i++)                        
                    if (o.equals(a[i]))                            
                    return i;
                    }        
                    return -1;
        }    
        public boolean contains(Object o) 
        {        
        return indexOf(o) != -1;
        }
    }
    
    ....
    其余代码省略
    ....

    针对 Add 操作: 该容器的该行为继承于 AbstractList 抽象类,直接或间接调用 add(int index, E element) 方法,抛出 UnsupportedOperationException

    // AbstractList 中的方法public boolean add(E e) {
        add(size(), e);    return true;
        }public void add(int index, E element) {    throw new UnsupportedOperationException();
    }public boolean addAll(int index, Collection<? extends E> c) {    boolean modified = false;
        Iterator<? extends E> e = c.iterator();    while (e.hasNext()) {
            add(index++, e.next());
            modified = true;
        }    return modified;
    }

    针对 remove 操作: 该容器的 remove 相关行为间接继承自 AbstractCollection 类, AbstractList 类并未完全重写(只重写了 clear 操作)它们,如下:

        //AbstractList 类中方法:
        //该方法继承自 List 接口的:remove(int index) 方法,是 List 特有的方法
        public E remove(int index) {    throw new UnsupportedOperationException();
        }    //AbstractList 类重写了 AbstractCollection 的 clear 方法
         public void clear() {
            removeRange(0, size());
        }   protected void removeRange(int fromIndex, int toIndex) {
            ListIterator<E> it = listIterator(fromIndex);        for (int i=0, n=toIndex-fromIndex; i<n; i++) {
                it.next();
                it.remove();           //直接调用了具体容器的迭代器(由 iterator() 方法返回)的 remove 方法   
            }
        }

       AbstractList 类未重写 remove(Object),removeAll(Collection<?>)retainAll(Collection<?>) ,这些方法实际上继承自 AbstractCollection 类:

        //AbstractCollection 类中方法:
        public boolean remove(Object o) {
        Iterator<E> e = iterator();    if (o==null) {        while (e.hasNext()) {        if (e.next()==null) {
                e.remove();            return true;
            }
            }
        } else {        while (e.hasNext()) {        if (o.equals(e.next())) {
                e.remove();           //直接调用了具体容器的迭代器(由 iterator() 方法返回)的 remove 方法   
                return true;
            }
            }
        }    return false;
        }public boolean removeAll(Collection<?> c) {    boolean modified = false;
        Iterator<?> e = iterator();    while (e.hasNext()) {        if (c.contains(e.next())) {
            e.remove();           //直接调用了具体容器的迭代器(由 iterator() 方法返回)的 remove 方法   
            modified = true;
            }
        }    return modified;
        } public boolean retainAll(Collection<?> c) {    boolean modified = false;
        Iterator<E> e = iterator();    while (e.hasNext()) {        if (!c.contains(e.next())) {
            e.remove();            //直接调用了具体容器的迭代器(由 iterator() 方法返回)的 remove 方法          
            modified = true;
            }
        }    return modified;
        }

       由上面两段代码可知,remove(Object),removeAll(Collection<?>)retainAll(Collection<?>) 和 clear() 这四种操作均直接调用了具体容器的迭代器(由 iterator() 方法返回)的 remove 方法 ,我们再看 AbstractList 类提供的 Iterator 中的 remove 方法:

       private class Itr implements Iterator<E> {
        /**
         * Index of element to be returned by subsequent call to next.
         */
        int cursor = 0;    /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         */
        int lastRet = -1;    /**
         * The modCount value that the iterator believes that the backing
         * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        int expectedModCount = modCount;    
        public boolean hasNext() 
        {            
        return cursor != size();
        }    public E next() 
        {
                checkForComodification();        
                try {
            E next = get(cursor);
            lastRet = cursor++;        
            return next;
            } 
            catch (IndexOutOfBoundsException e) 
            {
            checkForComodification();        
            throw new NoSuchElementException();
            }
        }    public void remove() {        
        if (lastRet == -1)        
        throw new IllegalStateException();
                checkForComodification();        
                try {
            AbstractList.this.remove(lastRet);            
            //直接调用 AbstractList 容器的 remove(int index) 方法,抛出 UnsupportedOperationException      
            if (lastRet < cursor)
                cursor--;
            lastRet = -1;
            expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {        
            throw new ConcurrentModificationException();
            }
        }    final void checkForComodification() {        
        if (modCount != expectedModCount)        
        throw new ConcurrentModificationException();
        }
        }

       综上可知,示例中的 Arrays.asList() 部分为何会导致那样的结果。

    以上就是Java Collection Framework -Collection接口的具体详解的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。

    前端(VUE)零基础到就业课程:点击学习

    清晰的学习路线+老师随时辅导答疑

    自己动手写 PHP MVC 框架:点击学习

    快速了解MVC架构、了解框架底层运行原理

    上一篇:深入理解享元模式示例代码(图文) 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• ❤️‍🔥接口自动化测试不想写代码?• Java数据结构之单链表与OJ题• 详细介绍Java正则表达式之单字符匹配和预定义字符• Java总结分享之反射、枚举、Lambda表达式• 一起来分析java设计模式之单例• 实例详解Java顺序表和链表
    1/1

    PHP中文网