首頁  >  文章  >  Java  >  Java Collection Framework -Collection介面的具體詳解

Java Collection Framework -Collection介面的具體詳解

黄舟
黄舟原創
2017-03-14 10:27:082449瀏覽


一、要點

  • Collection 介面

  • Collection 介面的預設實作:AbstractCollection

  • Optional operations(可選方法)


二、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

##   java.util.Collection 介面是描述Set 和List 集合類型的根接口,下面給出了Collection 的所有行為(不包括從
Object

# 繼承而來的方法) ,因此它們也刻畫了Set 或List 執行的所有行為(List 還有額外的功能)。 Collection 的行為分類與詳細清單如下 :     

##      

                                            表1. Collection 的操作

##可選運算Iterator iterator( )遍歷容器中的元素(統一的存取、遍歷方法,對外屏蔽了不同容器的差異性,依賴於具體實作boolean remove(Object)向容器移除指定元素,若移除動作發生,則傳回true(#可選操作boolean removeAll( 向容器移除參數中所有元素,若有移除動作發生,則傳回trueBoolean retainAll(Collection>void clearboolean contains(T)Boolean containsAll(Collection>
Function Introduction #Note
boolean add(T) 將指定元素新增至集合中,若未將該元素新增至容器,則傳回false #可選作業
boolean addAll(Collection extends T>) 新增參數中的所有元素,只要加入任意元素則傳回true
迭代器模式
若有重複對象,一次只刪除其中一個
Collection>) ##可選運算
)只儲存參數中的元素,只要Collection 發生改變,則傳回true 可選操作
()#移除容器中所有元素 可選操作
若容器持有該元素,則傳回true 依賴equals()
)#若容器持有參數中的所有元素,則傳回true 則依賴equals()
#######################################################################################################” #######object[] to###Array###()######傳回一個包含該容器所有元素的Object 型###陣列#########Array與Collection之間的橋樑,在AbstractCollection 中提供預設實現,子類別可以對它重寫############T[] toArray(T[] a)#######返回一個包含該容器所有元素的Object 型數組,且傳回結果的運行時類型與參數數組a 相同,而不單純是object######Array與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 构造方法。

    Java Collection Framework -Collection介面的具體詳解

  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 方法实现密切相关
    }

   对以上实现进行总结 :

  • 】:add, addAll 两个方法的实现是可选的,此处均默认 UnsupportedOperationException ;

  • 】:iterator 未提供具体实现,将实现延迟到具体容器,其对外屏蔽了不同容器的差异性,以统一的方式对容器访问、遍历 ;

  • 】:remove(Object),removeAll(Collection>)retainAll(Collection>) 和 clear() 四个方法的实现均直接调用了具体容器的迭代器(由 iterator() 方法返回)的 remove 方法 ;

  • 】:contains(Object o) 和 containsAll(Collection> c) 两个方法的实现均直接或间接调用了元素的 equals 方法 ;

  • 基本方法】:size 方法实现与具体容器相关, isEmpty 方法的实现均直接调用了 size 方法 ;

  • 容器与数组转换】:分别提供与泛型 toArray 方法 和 原生 toArray 方法;


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

  • 执行各种不同的添加和移除方法在 Collection 接口中都是可选操作,因为他们会改变容器的结构;

  • Collection 接口中的读取方法都不是可选操作 ;


四、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