Sammlung Schnittstelle
Sammlungsschnittstelle Standardimplementierung: AbstractCollection
Optionale Operationen (optionale Methode)
1 Beschreibung der Collection-Schnittstelle
Die Collection-Schnittstelle ist die Root-Schnittstelle in der Collection-Hierarchie. Eine Sammlung stellt eine Menge von Objekten dar, die auch als Elemente der Sammlung bezeichnet werden. Einige Sammlungen (Liste, Warteschlange) erlauben doppelte Elemente, andere (Set) nicht. Einige Sammlungen (Liste, Warteschlange) sind geordnet, während andere (Set) ungeordnet sind. Das JDK bietet keine direkte Implementierung dieser Schnittstelle: Es stellt Implementierungen spezifischerer Unterschnittstellen wie Set und List bereit, die von der Collection-Schnittstelle erben . Diese Schnittstelle wird normalerweise verwendet, um Sammlungen (polymorph) zu übergeben und mit diesen Sammlungen zu arbeiten überall dort, wo maximale Allgemeingültigkeit erforderlich ist.
Alle allgemeinen Collection-Implementierungsklassen (die Collection normalerweise indirekt über eine ihrer Unterschnittstellen implementieren) sollten zwei „Standard“-Konstruktormethoden bereitstellen: Einer ist ein void-Konstruktor (kein Parameter), der zum Erstellen einer leeren Sammlung verwendet wird. Der andere ist ein Konstruktor mit einem einzelnen Parameter vom Typ Sammlung, der zum Erstellen einer neuen Sammlung mit denselben Elementen verwendet wird als Parametersammlung. Tatsächlich ermöglicht Letzteres dem Benutzer, jede Sammlung zu kopieren, um eine äquivalente Sammlung des gewünschten Implementierungstyps zu generieren. Obwohl diese Konvention nicht erzwungen werden kann (da Schnittstellen keine Konstruktoren enthalten können), halten sich alle gängigen Collection-Implementierungen in den Java-Plattformbibliotheken daran.
Die in dieser Schnittstelle enthaltenen „destruktiven“ Methoden beziehen sich auf die Methoden, die die Sammlung ändern können, auf die sie zugreifen. Wenn diese Sammlung den Vorgang nicht unterstützt, werden diese Methoden so angegeben, dass sie UnsupportedOperation auslösen Ausnahme(optionaler Vorgang).
Viele Methoden in der Collections Framework-Schnittstelle werden basierend auf der Methode equal definiert.
Jeder Container im Collections Framework ist nicht-threaded sicher.
Die Collection-Schnittstelle ist wie folgt definiert:
public interface Collection<E> extends Iterable<E> { ... }
Verhalten
Function | Introduction | Note |
---|---|---|
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 相同,而不单纯是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() 部分为何会导致那样的结果。
Das obige ist der detaillierte Inhalt vonJava Collection Framework – Detaillierte Erläuterung der Collection-Schnittstelle. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!