Collection Interface
Collection インターフェースのデフォルト実装: AbstractCollection
オプションの操作 (オプションのメソッド)
1、CollectionインターフェースのJDK説明
Collectionインターフェースは、Collection階層のルートインターフェースです。コレクションは オブジェクト のセットを表し、コレクションの要素とも呼ばれます。一部のコレクション (List、Queue) では要素の重複が許可されますが、その他のコレクション (Set) では許可されません。一部のコレクション (リスト、キュー) は順序付けされていますが、その他のコレクション (セット) は順序付けされていません。 JDK は、このインターフェースの直接実装を提供しません。JDK は、Collection インターフェースから継承する、Set や List などのより具体的なサブインターフェースの実装を提供します。このインターフェースは通常、最大限の汎用性が必要な場合にコレクションを (多態的に) 渡し、これらのコレクションを操作するために使用されます。
すべての一般的な Collection 実装クラス (通常、そのサブインターフェイスの 1 つを通じて間接的に Collection を実装する) は、 2 つの「標準」 コンストラクター メソッド を提供する必要があります。 1 つは、空のコレクションを作成するための void (パラメーターなし) コンストラクターです。 もう 1 つは、Collection 型の単一パラメーターを持つコンストラクターで、パラメーターと同じ要素を持つ新しいコレクションを作成するために使用されます。実際、後者を使用すると、ユーザーは任意のコレクションをコピーして、目的の実装タイプの同等のコレクションを生成できます。この規則は (インターフェイスにコンストラクターを含めることができないため) 強制することはできませんが、Java プラットフォーム ライブラリ内のすべての一般的な Collection 実装はこの規則に従っています。
このインターフェースに含まれる「破壊的」メソッドは、操作するコレクションを変更できるメソッドを指します。このコレクションが操作をサポートしていない場合、これらのメソッドは UnsupportedOperationException (オプションの操作) をスローするように指定されます。
Collections Framework インターフェイスの多くのメソッドは、equals メソッドに基づいて定義されています。
Collections Framework の各コンテナーはスレッド化されていません安全です。
Collection インターフェースは次のように定義されています:
public interface Collection<E> extends Iterable<E> { ... }
以下の詳細リストにダクトスルーします。
関数 | 概要 | 注 |
---|---|---|
boolean add(T) | 要素がコンテナに追加されていない場合は、falseを返します | オプションの操作 |
boolean addAll(Collection extends T> )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 を返しますオプションの操作 | |
コンテナ内の要素をトラバースします (統合アクセスおよびトラバーサル メソッド、さまざまなコンテナの違いを外部からシールドします、イテレータ パターン) | 特定の実装に依存します | boolean Remove(Object) |
オプションの操作 | boolean RemoveAll( | |
オプションの操作 | 。 | Boolean restartAll( |
オプションの操作 | void clear |
Collection>
)🎜🎜コンテナがパラメータ内のすべての要素を保持している場合、 trueを返します🎜 🎜equals()に依存します🎜🎜🎜🎜object[] to🎜Array🎜()🎜🎜コンテナのすべての要素を含むオブジェクト型🎜配列を返します🎜🎜🎜配列とコレクションの間のブリッジ、デフォルトの実装は次のとおりですAbstractCollection で提供され、サブクラスはそれをオーバーライドできます🎜🎜🎜🎜T[] toArray(T[] a)🎜🎜 はコンテナのすべての要素を含む Object 配列を返し、戻り結果のランタイム型はパラメータ配列と同じですa、そして、これは単純に object🎜🎜Array と Collection の間のブリッジではなく、AbstractCollection でデフォルトの実装を提供し、サブクラスはそれをオーバーライドできます🎜🎜🎜🎜int size()🎜🎜はコンテナ内の要素の数を返します🎜🎜特定の実装に依存します🎜 🎜🎜🎜boolean isEmpty()🎜🎜コンテナが空の場合、trueを返します🎜🎜size()に依存します🎜🎜🎜🎜注意到,其中不包含随机访问所选择的元素 get() 方法,因为 Collection 包含 Set,而 Set 是自己维护顺序的。因此,若想访问、遍历 Collection 中的元素,则必须使用迭代器; 另外,这些方法的默认实现(AbstractCollection)均直接或间接使用了迭代器。
1、JDK 对 AbstractCollection 抽象类的描述
此抽象类提供 Collection 接口的骨干实现,以最大限度地减少实现此接口所需的工作。 要实现一个不可修改的 collection,编程人员只需扩展此类,并提供 iterator 和 size 方法的实现。(iterator 方法返回的迭代器必须实现 hasNext 和 next。); 要实现可修改的 collection,编程人员必须另外重写此类的 add 方法(否则,会抛出 UnsupportedOperationException),iterator 方法返回的迭代器还必须另外实现其 remove 方法(AbstractCollection 的移除方法间接调用迭代器的移除方法)。按照 Collection 接口规范中的建议,编程人员通常应提供一个 void (无参数)和 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 接口中的读取方法都不是可选操作 ;
1、简述
执行各种不同的添加和移除方法在 Collection 接口中都是可选操作, 这意味着实现类并不需要为这些方法提供功能定义。
这是一种很不寻常的接口定义方式。正如你所看到的那样,接口是面向对象设计中的契约,它声明 “无论你选择如何实现该接口,我保证你可以向该接口发送这些消息。”但可选操作违反这个非常基本的原则,它声明调用某些方法将不会执行有意义的行为,相反,他们会抛出异常。
为什么会将方法定义为可选呢? 因为这样做可以防止在设计中出现接口爆炸的情形。容器类库中的设计看起来总是为了描述每个主题的变体,而最终患上了令人困惑的接口过剩症。甚至这么做仍不能捕获接口的各种特例,因为总是有人会发明新的接口。“未获支持的操作” 这种方式可以实现 Java 容器类库的一个重要目标:容器应易学易用。未获支持的操作是一种特例,可以延迟到需要时实现。但是,为了让这种方式能够工作:
UnsupportedOperationException 必须是一种罕见事件。即,对于大多数类而言,所有操作都应该可以工作,只有在特例(例如,通过Arrays.asList()所得到的容器)中才会有未获支持的操作。在 Java 容器类库中确实如此,因为你在 99% 的时间里使用的容器类,如 ArrayList、LinkedList、HashSet 和 HashMap,以及其他的具体实现,都支持所有操作。这种设计留下了一个“后门”,如果你想创建新的 Collection, 但是没有为 Collection 接口中的所有方法都提供有意义的定义,那么它仍旧适合现有类库。
如果一个操作是未获支持的,那么在实现接口的时候可能就会导致 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 中国語 Web サイトの他の関連記事を参照してください。