JDK8源码阅读笔记
切换暗/亮/自动模式 切换暗/亮/自动模式 切换暗/亮/自动模式 返回首页

Collection



Collection接口是集合层次结构中的根接口。集合表示一组对象,每个对象称为元素。有些集合允许重复元素,有些则不允许。有些集合是有序的,有些是无序的。

JDK没有提供该接口的任何直接实现:它提供了更具体的子接口的实现,如SetList。此接口通常用于传递集合并在需要最大通用性的地方操作它们。

Bagsmultiset(可能包含重复元素的无序集合)应该直接实现这个接口。

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

该接口中包含一些具有“破坏性”的方法,也就是这些方法在被调用时操作集合,但该集合并不支持该操作,此时这些方法将抛出UnsupportedOperationException。在这种情况下,如果调用对集合没有影响,则这些方法可能(但不是必需)抛出UnsupportedOperationException。例如,对不可修改的集合调用addAll方法可能会(但并不一定)引发异常(如果要添加的集合为空的话)。

一些集合实现对它们可能包含的元素有所限制。例如,某些实现禁止空元素,而有些实现对其元素类型有限制。尝试添加不匹配的元素会引发未经检查的异常,通常为NullPointerExceptionClassCastException。尝试查询集合中不匹配的元素是否存在可能会引发异常,或者可能仅返回false。一些实现将表现出前一种行为,而某些实现将表现出后者。更普遍的是,尝试对不匹配的元素进行操作,该操作的完成不会将不合格元素插入集合中,这可能会导致异常或成功,具体取决于实现方式。此类异常在此接口的规范中标记为“可选”。

同步策略是由每个集合自己决定的。在没有强有力保证的实现中,调用另一个线程正在改变的集合上的任何方法都可能导致未知的行为;这包括直接调用,将集合传递给可能执行调用的方法,以及使用现有的迭代器检查集合。

集合框架接口中的许多方法都是根据Objectequals方法定义的。例如,contains方法的规范中说:“当且仅当此集合包含至少一个元素e满足(o==null ? e==null : o.equals(e))时,返回true”。此规范不应解释为暗示使用非空参数o调用Collection.contains时将导致对任何元素e都会调用o.equals(e)。各实现可以自由地进行优化,从而避免equals调用,例如,首先比较两个元素的哈希码。(ObjecthashCode()规范保证哈希码不相等的两个对象不能相等。)更普遍的说,只要实现者认为合适,各种集合框架接口的实现都可以自由利用基础Object方法的指定行为。

一些对集合执行递归遍历的操作可能会失败,但对于集合直接或间接包含其自身的自引用实例除外,包括clone()equals()hashCode()toString()方法。实现类可以选择性地处理自引用场景,但是大多数当前的实现都没有这样做。

该接口是Java集合框架的成员接口。

默认的方法实现(继承的或其他)不应用任何同步协议。如果一个集合实现有一个特定的同步协议,那么它必须覆盖默认实现来应用该协议。

1. int size()

返回当前集合内元素的数量。如果数量超过Integer.MAX_VALUE则返回Integer.MAX_VALUE

2. boolean isEmpty()

如果当前集合内没有元素,则返回true

3. boolean contains(Object o)

当集合包含指定的元素时返回true。更准确的说,当且仅当集合中至少存在一个元素e能够满足(o==null ? e==null : o.equals(e))时返回true

如果指定元素的类型与此集合不兼容,则抛出ClassCastException

如果指定的元素o为空,则抛出NullPointerException

4. Iterator<E> iterator()

返回此集合中元素的迭代器。没有关于元素返回顺序的保证(除非此集合是某个提供保证的类的实例)。

5. Object[] toArray()

返回一个包含此集合中所有元素的数组。如果此集合保证其迭代器返回其元素的顺序,则此方法必须按相同的顺序返回元素。

返回的数组将是“安全的”,因为此集合不维护对其的引用。 (换句话说,即使此集合是基于数组的,此方法也必须分配一个新数组)。因此,调用者可以自由修改返回的数组。

此方法充当基于数组的api和基于集合的api之间的桥梁。

6. <T> T[] toArray(T[] a)

返回一个数组,该数组包含此集合中的所有元素。返回数组的运行时类型是指定数组的运行时类型。如果集合符合指定的数组,则在其中返回集合。否则,将使用指定数组的运行时类型和此集合的大小分配新数组。

如果指定的数组能够容纳集合的所有元素并有剩余空间(即数组中的容量大于集合),则紧接集合结束后的数组中的元素设置为null。(如果调用者知道列表不包含任何null元素,则这对于确定列表的长度很有用。)

如果此集合保证其迭代器返回元素的顺序,则此方法必须按相同的顺序返回元素。

toArray()方法类似,此方法充当基于数组的API和基于集合的API之间的桥梁。此外,此方法允许对输出数组的运行时类型进行精确控制,并且在某些情况下可以用来节省分配成本。

假设x是一个已知仅包含字符串的集合。以下代码可用于将集合转储到新分配的String数组中:

String[] y = x.toArray(new String[0]);

请注意,toArray(new Object[0])在功能上与toArray()相同。

如果指定数组的运行时类型不是此集合中每个元素的运行时类型的超类型,则抛出ArrayStoreException

如果参数中指定的数组anull,则抛出NullPointerException

7. boolean add(E e)

确保此集合包含指定的元素(可选操作)。如果此集合由于调用该方法而更改,则返回true。 (如果此集合不允许重复并且已经包含指定的元素,则返回false。)

支持此操作的集合可能会对可以添加到此集合的元素加以限制。特别是,某些集合将拒绝添加null元素,而其他集合将对可能添加的元素类型施加限制。集合类应在其文档中明确指定对可以添加哪些元素的限制。

如果某个集合由于除了已经包含该元素以外的其他原因拒绝添加特定元素,则它必须抛出异常(而不是返回false)。这保留了在此调用返回后集合始终包含指定元素的不变性。

如果当前集合不支持add操作,则抛出UnsupportedOperationException

如果指定元素的类阻止将其添加到此集合中,则抛出ClassCastException

如果指定的元素为null,并且此集合不允许使用null元素,则抛出NullPointerException

如果元素的某些属性阻止将其添加到此集合中,则抛出IllegalArgumentException

如果由于插入限制当前无法添加该元素,则抛出IllegalStateException

8. boolean remove(Object o)

如果指定的元素在此集合中存在,则从此集合中删除它(可选操作)。

更正式地讲,如果存在当前集合中存在一个或多个元素e可以满足(o==null ? e==null : o.equals(e)),则删除该元素e(注:此处源码中明确指出是满足条件的元素全部删除,还是删除其中的一个,它只是说:removes an element e such that ···)。如果此集合包含指定的元素(或者等效地,如果此集合由于调用而更改),则返回true

如果当前集合不支持remove操作,则抛出UnsupportedOperationException

如果指定元素的类型与此集合不兼容,则抛出ClassCastException

如果指定的元素为null,并且此集合不允许使用null元素,则抛出NullPointerException

9. boolean containsAll(Collection c)

如果此集合包含指定集合中的所有元素,则返回true

如果指定集合中一个或多个元素的类型与此集合不兼容,则抛出ClassCastException

如果指定的集合包含一个或多个null元素,并且此集合不允许null元素,则抛出NullPointerException

10. boolean addAll(Collection<? extends E> c)

将指定集合中的所有元素添加到此集合中(可选操作)。如果在操作进行过程中修改了指定的集合,则此操作的行为是不确定的。 (这意味着如果指定的集合就是本集合,并且本集合是非空的,则此次调用的行为是不确定的。)

11. boolean removeAll(Collection c)

删除也包含在指定集合中的所有此集合的元素(可选操作)。在此调用返回之后,此集合将不包含与指定集合相同的元素。

12. default boolean removeIf(Predicate<? super E> filter)

1.8版本的JDK中新出的方法。删除此集合中满足给定断言的所有元素。在迭代过程中或断言中引发的错误或运行时异常将中继给调用方。

默认实现使用其iterator遍历集合的所有元素。使用Iteratorremove()方法删除每个匹配的元素。如果集合的迭代器不支持删除,则将在第一个匹配元素上抛出UnsupportedOperationException

源码如下:

default boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    boolean removed = false;
    final Iterator<E> each = iterator();
    while (each.hasNext()) {
				// 对每个元素执行给定的断言,命中则删除该元素
        if (filter.test(each.next())) {
            each.remove();
            removed = true;
        }
    }
    return removed;
}

13. boolean retainAll(Collection c)

只保留此集合中包含在指定集合中的元素(可选操作)。换句话说,从该集合中删除指定集合中不包含的所有元素。

14. void clear()

从此集合中删除所有元素(可选操作)。此方法返回后,集合将为空。

15. boolean equals(Object o)

将指定对象与此集合进行比较以判断是否相等。

尽管Collection接口没有为Object.equals的常规规定添加任何规定,但是“直接”实现Collection接口的程序员(换句话说,创建Collection但不是SetList的类)如果选择覆盖Object.equals的话则必须要当心。其实没有必要这样做,最简单的方法是依靠Object的实现,但是实现者可能希望实现“值比较”来代替默认的“引用比较”。(ListSet接口要求进行值比较。)

Object.equals方法的通用规范规定,equals必须是对称的(换句话说,当且仅当b.equals(a)a.equals(b))。 List.equalsSet.equals的约定规定,List仅等于其他List,并且Set仅等于其他Set。因此,如果一个集合类它实现的既不是List也不是Set接口,那么当这个集合与ListSet比较时,它自定义的equals方法必须返回false。(按照这个逻辑,不可能编写一个类能够同时正确地实现ListSet接口的类。)

16. int hashCode()

返回当前集合的hash code值。尽管Collection接口没有在Object.hashCode方法的常规约定之外添加任何规定,但编程者应注意,任何覆盖Object.equals方法的类还必须重写Object.hashCode方法,以便满足Object.hashCode方法的常规约定。特别是,c1.equals(c2)意味着c1.hashCode()==c2.hashCode()

17. default Spliterator<E> spliterator()

在此集合中的元素上创建一个Spliterator

实现中应该说明拆分器报告的特征值。如果拆分器报告Spliterator.SIZED并且该集合不包含任何元素,则就不需要报告这些特征值。

子类应该覆盖默认的实现,以返回一个更高效的拆分器。为了保持stream()parallelStream()方法的预期惰性行为,拆分器应该具有IMMUTABLECONCURRENT的特征,或者是后期绑定。

如果这些都不可行,则覆盖的类应描述拆分器记录的绑定和结构干扰策略,并应覆盖stream()parallelStream()方法,以使用拆分器的Supplier创建流,如:

Stream<E> s = StreamSupport.stream(() -> spliterator(), spliteratorCharacteristics)

这些要求确保stream()parallelStream()方法生成的流将反映在终端流操作启动时集合的内容。

默认实现通过集合的Iterator创建一个延迟绑定的拆分器。该拆分器继承了集合迭代器快速失败的特性。

创建的Spliterator报告Spliterator.SIZED

创建的Spliterator另外还报告了Spliterator.SUBSIZED

如果拆分器没有覆盖任何元素,那么除了SIZEDSUBSIZED之外的其他特征值的报告,不会帮助客户端控制、专门化或简化计算。然而,这确实允许对空集合共享不可变的空拆分器实例(参见Spliterators.emptySpliterator()),并允许客户端确定这样的拆分器是否不包含元素。

源码如下:

@Override
default Spliterator<E> spliterator() {
    return Spliterators.spliterator(this, 0);
}

18. default Stream<E> stream()

返回以该集合为源的顺序Stream

spliterator()方法无法返回IMMUTABLECONCURRENT或延迟绑定的拆分器时,应重写此方法。 (有关详细信息,请参见spliterator()

默认实现通过集合的Spliterator创建顺序的Stream

源码如下:

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

19. default Stream<E> parallelStream()

返回以此集合为源的可能并行的Stream。此方法也允许返回顺序Stream

spliterator()方法无法返回IMMUTABLECONCURRENT或延迟绑定的拆分器时,应重写此方法。 (有关详细信息,请参见spliterator()

默认实现从集合的Spliterator创建并行的Stream

default Stream<E> parallelStream() {
    return StreamSupport.stream(spliterator(), true);
}