Collection源码阅读笔记

引言

Collection接口和Map接口是Java集合框架的根接口,List、Queue、Set等接口都实现了Collection接口,其中有的集合允许重复元素,有的集合允许有序。正是因为我们需要用到具有不同特性的集合,所以jdk不提供对Collection接口的直接实现,而是用上述那些更加具体的接口来继承Collection接口,再让具体的实现类去实现这些接口。总之,Collection接口只是从所有类型的集合中抽取出来的共性,而List、Queue、Set接口则是从某种类型的集合(有序or无序,可重复or不可重)中抽取出来的共性,像List接口就是专门为列表这种集合抽取出来的共性。

Collection是面向接口编程的典范,通过它可以在多种实现类间转换,这也是面向对象编程的魅力之一。

一、接口定义

public interface Collection<E> extends Iterable<E>

Collection接口直接继承于Iterable接口,因此所有Collection集合类都支持for-each循环。

二、方法定义

对于集合来说,我们所希望的一些操作无非:统计元素个数、元素的增删改查、判断某个元素的存在性、清空等等,Collection对这些常用操作进行提取封装。

(一)查询方法

(1)

int size();

返回集合中元素个数,如果个数大于Integer.MAX_VALUE,就返回Integer.MAX_VALUE

(2)

boolean isEmpty();

判断集合是否为空,即元素个数是否为0,若为空则返回true,否则返回false

(3)

boolean contains(Object o);

判断集合中是否包含指定元素(利用equals()方法)

当集合中至少存在一个元素e使得(o==null ? e==null : o.equals(e))满足时,返回true
当指定元素的类型与该集合不兼容,抛出ClassCastException;
当指定元素为null时并且此集合不允许null元素时,抛出NullPointerException
(4)
Iterator<E> iterator();

返回可以在集合之上迭代的迭代器

(5)

Object[] toArray();

返回一个包含集合所有元素的数组

返回的数组是新分配的,集合没有保持对这个数组的引用,因此方法的调用者可以自由、安全地修改这个数组

<T> T[] toArray(T[] a);

跟上面一样也是返回一个包含集合所有元素的数组

不过,数组的类型是指定的:返回数组的运行时类型与指定数组的运行时类型相同。如果指定数组的大小可以容纳集合的所有元素,则返回时依然返回包含集合所有元素的指定数组(当指定数组的大小大于集合的元素个数,则数组下表为集合size之后的所有元素都被赋值为null);否则,将分配一个具有指定数组的运行时类型、以及此集合大小的新数组。

也就是说,此方法可以用于精确控制返回数组的类型,并且分配数组指定的大小。

当指定数组为null,抛出NullPointerException异常;

当指定数组的运行时类型不是集合中每个元素的运行时类型的父类时,抛出ArrayStoreException

toArray(new Object[0])作用等同于toArray()方法,当我们想要返回集合x的String类型数组时,只需

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

两个toArray()方法充当了数组和集合之间的桥梁,而我们转换各种类型集合,也是经常用这两个方法。

二、修改操作

boolean add(E e);

添加指定元素到集合中。

如果添加成功(集合在此方法调用结束后有所改变),返回true;如果集合不允许重复并且已经存在指定元素时,返回false

不同类型的集合在添加元素时可能有所限制,比如说,有的集合拒绝添加null元素,有的集合在添加元素的类型上做了一些限制。如果集合有任何理由拒绝添加元素(除了不允许重复而集合中已经有指定元素的情况),应该抛出一个异常,而不是仅仅返回false。

当某种类型集合不支持此方法,抛出UnsupportedOperationException;

当集合不允许添加指定元素的类型数据时,抛出ClassCastException;

当指定元素为null并且集合不允许null元素时,抛出NullPointerException;

当指定元素的一些参数阻止该元素添加到集合中时,抛出IllegalArgumentException;

当指定元素因为一些插入限制而无法添加到集合中时,抛出IllegalStateException

boolean remove(Object o);

删除集合中与对象o相等的一个元素,即,删除集合中一个或者多个满足  (o==null ? e==null : o.equals(e))的元素e

如果存在这样的元素并删除成功,返回true;否则返回false

当指定元素为null并且集合不允许null元素时,抛出NullPointerException;

当指定元素的类型与集合不兼容,抛出ClassCastException;

当集合不支持此操作时,抛出UnsupportedOperationException

三、批量操作

(1)

boolean containsAll(Collection<?> c);

判断集合是否包含指定结合的所有元素

如果包含,则返回true;否则返回false

当指定集合的一个或多个元素与此集合不兼容时,抛出ClassCastException;

当指定集合中有一个或多个null元素时,并且此集合不允许null元素,则抛出NullPointerException

(2)

boolean addAll(Collection<? extends E> c);

添加指定集合的所有元素到此集合中

如果调用此方法之后,集合发生改变,则返回true

但要注意,当此操作在进行中时,指定集合被修改,那么此操作的行为将是undefined。比如说,当指定集合是此集合本身,并且此集合不为空时。

当某种类型集合不支持此方法,抛出UnsupportedOperationException;

当集合不允许添加指定集合的一个元素的类型数据时,抛出ClassCastException;

当指定集合的一个元素为null并且集合不允许null元素时,抛出NullPointerException;

当指定集合的一个元素的一些参数阻止该元素添加到集合中时,抛出IllegalArgumentException;

当指定集合的一个元素因为一些插入限制而无法添加到集合中时,抛出IllegalStateException

(3)

boolean removeAll(Collection<?> c);

删除集合中包含在指定集合中的所有元素

如果在调用此方法之后集合改变了,则返回true

当某种类型集合不支持此方法,抛出UnsupportedOperationException;

当指定集合有一个或多个元素与此集合不兼容时,抛出ClassCastException;

当指定集合的一个或多个元素为null并且集合不允许null元素时,抛出NullPointerException;

(4)

boolean retainAll(Collection<?> c);

只保留集合中包含在指定集合中的所有元素,此方法与上面的方法正好相反

如果在调用此方法后,集合改变了,则返回true

当某种类型集合不支持此方法,抛出UnsupportedOperationException;

当指定集合有一个或多个元素与此集合不兼容时,抛出ClassCastException;

当指定集合的一个或多个元素为null并且集合不允许null元素时,抛出NullPointerException;

(5)

void clear();

删除此集合中的所有元素,删除成功之后集合为空

当此集合不支持此方法,抛出UnsupportedOperationException

四、比较和哈希操作

 (1)

boolean equals(Object o);

比较集合与指定对象是否相等

这里将此方法抽象,是为了保证所有子类都重写此方法,以保证equals的正确行为。至于怎样才算是相等,则取决于具体的实现类。

当我们在编写一个Collection接口的实现类时,如果对相等性判断没有特定的需求,那么直接实现Collection即可,因为实现类默认继承Object类的equals()方法(比较两个对象是否是同一个对象);如果需要进行的是“值比较”,那么就需要重写此方法。

Object类的equals()方法的总体规则是,a.equals(b)当且仅当b.equals(a),对于List、Set的equals(),则有:list只与list相等,set只与set相等。因此,我们在自定义equals()方法时,当list和set进行比较时必须返回false。同样的逻辑,我们不能让一个类同时实现Set和List接口。

(2)

int hashCode();

返回集合的哈希码值。

这里将此方法抽象,同样是为了保证所有子类都重写此方法,以保证hashCode的正确行为

注意:任何类重写Object的equals()方法,必须同时重写Object的hashCode()方法,以满足以下总体规则:

c1.equals(c2)意味着c1.hashCode()==c2.hashCode()有相同的结果

五、其他方法

jdk 8还在Collection中提供一些默认实现的方法

(1)

    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;
    }

删除集合中符合给定条件filter的元素(按照迭代器的访问顺序)

当有一个或多个元素被删除,则返回true;否则返回false

可以看到,此方法使用迭代器遍历集合的所有元素,每个符合给定条件的元素都会从集合中删除(删除时通过迭代器的remove()方法删除)

当给定的filter为null,则抛出NullPointerException;

当元素无法从集合中删除时,抛出UnsupportedException

(2)

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

创建一个在集合的元素之上的Spliterator,此方法覆盖了Iterable接口的spliterator()方法

Spliterator接口可以将stream分解成多个部分,这些部分可以被并行处理。在实际使用中,我们很少直接使用Spliterator,而是将它按照类似Iterator一样使用。比如,Spliterator<T> spliterator = list.spliterator();

Collection接口中还提供了stream()、parallelStream()方法,里面通过调用spliterator()方法得到并利用了Spliterator,这两个方法有助于并行处理集合数据。

(3)

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

 返回一个以集合作为源的顺序流

(4)

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

返回一个以集合作为源的可能并行的流 

 

posted @ 2019-07-03 22:18  JeremyChan  阅读(222)  评论(0编辑  收藏  举报