Java集合源码分析之超级接口:Collection

简介: CollectionCollection是List、Queue和Set的超集,它直接继承于Iterable,也就是所有的Collection集合类都支持for-each循环。除此之外,Collection也是面向接口编程的典范,通过它可以在多种实现类间转换,这也是面向对象编程的魅力之一。方法定义在阅读源码前,我们可以先自行想象一下,如果我们想封装下数组或链表以方便操作,我们需要封装哪些功能呢?比如:统计大小、插入或删除数据、清空、是否包含某条数据,等等。而Collection就是对这些常用操作进行提取,只是其很全面、很通用。下面我们看看它都提供了哪些方法。

Collection
Collection是List、Queue和Set的超集,它直接继承于Iterable,也就是所有的Collection集合类都支持for-each循环。除此之外,Collection也是面向接口编程的典范,通过它可以在多种实现类间转换,这也是面向对象编程的魅力之一。

方法定义
在阅读源码前,我们可以先自行想象一下,如果我们想封装下数组或链表以方便操作,我们需要封装哪些功能呢?比如:统计大小、插入或删除数据、清空、是否包含某条数据,等等。而Collection就是对这些常用操作进行提取,只是其很全面、很通用。下面我们看看它都提供了哪些方法。

//返回集合的长度,如果长度大于Integer.MAX_VALUE,返回Integer.MAX_VALUE
int size();

//如果集合元素总数为0,返回true
boolean isEmpty();

//判断集合中是否包含指定的元素,其依据是equals()方法
boolean contains(Object o);

//返回一个包含集合中所有元素的数组
Object[] toArray();

//与上个类似,只是增加了类型的转换
T[] toArray(T[] a);

//向集合中加入一个元素,如果成功加入则返回true,如果加入失败,或者因集合本身已经包含同个元素而不再加入时,返回false
boolean add(E e);

//从集合中删除指定元素的单个实例
boolean remove(Object o);

//如果集合包含指定集合中的所有元素,返回true
boolean containsAll(Collection<?> c);

//把指定集合中的所有元素添加到集合中,但在此期间,如果指定的集合发生了改变,可能出现意想不到的事情
boolean addAll(Collection<? extends E> c);

//从集合中删除所有包含在指定集合中的元素
boolean removeAll(Collection<?> c);

//仅保留集合中包含在指定集合中的元素
boolean retainAll(Collection<?> c);

//清空集合
void clear();

//将此方法抽象,是保证所有子类都覆写此方法,以保证equals的正确行为
boolean equals(Object o);

//同上
int hashCode();

//这个方法在JDK1.8中提供了默认的实现,会使用Iterator的形式删除符合条件的元素
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;

}
超级实现类:AbstractCollection
在Collection中定义的许多方法,根据现有的定义以及继承的Iterable,都可以在抽象类中实现,这样可以减少实现类需要实现的方法,这个抽象类就是AbstractCollection。

首先我们关注下其文档,里面有两句说明可能会影响我们的继承:

To implement an unmodifiable collection, the programmer needs only to extend this class and provide implementations for the iteratorand size methods. (The iterator returned by the iterator method must implement hasNext and next.)

To implement a modifiable collection, the programmer must additionally override this class's add method (which otherwise throws an UnsupportedOperationException), and the iterator returned by the iterator method must additionally implement its remove method.

大体意思是说,如果要实现一个不可修改的集合,只需要重写iterator和size接口就可以,并且返回的Iterator需要实现hasNext和next。而要实现一个可以修改的集合,还必须重写add方法(默认会抛出异常),返回的Iterator还需要实现remove方法。

方法定义
//这个毫无疑问,是可以直接获取的
public boolean isEmpty() {

return size() == ;

}

//这个方法因为Iterator的存在,可以进行一致性封装,这里需要注意的是对象的比较是通过equals方法,因为调用到了it.next()与it.hasNext(),这也是为什么手游账号出售平台文档注释会写实现集合类需要重写Iterator的这两个方法。
public boolean contains(Object o) {

Iterator<E> it = iterator();
if (o==null) {
    while (it.hasNext())
        if (it.next()==null)
            return true;
} else {
    while (it.hasNext())
        if (o.equals(it.next()))
            return true;
}
return false;

}

//和contains类似,也是通过Iterator实现的,但其会调用it.remove()方法,这也是为什么文档注释会写实现可以修改的集合类时需要重写Iterator的remove方法。
public boolean remove(Object o) {

//...省略,这里调用了it.remove()方法

}
类似的方法还有containsAll(Collection<?> c)、addAll(Collection<? extends E> c)、removeAll(Collection<?> c)、retainAll(Collection<?> c)和clear()等,都需要利用到Iterator的特性,这里就不再一一赘述了。

另外还有一个toArray()的方法实现略微不同,可以看看其具体实现。

//这个实现相对复杂一些,可以看到扩容最主要的手段是Arrays.copyOf()方法,
//也就是需要将原数组通过复制到新的数组中来实现的。
//注意这里返回的顺序和Iterator顺序一致
//在这里实现是为了方便不同具体实现类互相转换,我们在后续会多次见到此方法
public Object[] toArray() {

//先根据当前集合大小声明一个数组
Object[] r = new Object[size()];
Iterator<E> it = iterator();
for (int i = ; i < r.length; i++) {
    //集合元素没那么多,说明不需要那么大的数组
    if (! it.hasNext()) 
        return Arrays.copyOf(r, i); //仅返回赋完值的部分
    r[i] = it.next();
}
//元素比从size()中获取的更多,就需要进一步调整数组大小
return it.hasNext() ? finishToArray(r, it) : r;

}

private static T[] finishToArray(T[] r, Iterator<?> it) {

//记录当前大小
int i = r.length;
while (it.hasNext()) {
    int cap = r.length;
    //r的长度不够,继续分配
    if (i == cap) {
        //扩充方式为cap+cap/2+1,也就是1.5倍扩容
        int newCap = cap + (cap >> 1) + 1;
        // 超过了最大容量,MAX_ARRAY_SIZE=Integer.MAX_VALUE-8
        if (newCap - MAX_ARRAY_SIZE > )
            //重新设置cap的值
            newCap = hugeCapacity(cap + 1);

        //对r进行扩容
        r = Arrays.copyOf(r, newCap);
    }
    //赋值,进入下一轮循环
    r[i++] = (T)it.next();
}
// 由于之前扩容是1.5倍进行的,最后再将其设置到和r实际需要的相同
return (i == r.length) ? r : Arrays.copyOf(r, i);

}

private static int hugeCapacity(int minCapacity) {

if (minCapacity < ) // 超过了最大正整数,也就是负数
    throw new OutOfMemoryError
        ("Required array size too large");
return (minCapacity > MAX_ARRAY_SIZE) ?
    Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;

}

//和toArray()方法类似,就不再赘述,具体可以查看源码
public T[] toArray(T[] a) {

//...

}
除了以上这些方法,AbstractCollection还实现了toString方法,其是通过StringBuilder拼接了每个元素的toString完成的,也并不复杂。这里可以看下其源码:

public String toString() {

Iterator<E> it = iterator();
if (! it.hasNext())
    return "[]";

StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
    E e = it.next();
    sb.append(e == this ? "(this Collection)" : e);
    if (! it.hasNext())
        return sb.append(']').toString();
    sb.append(',').append(' ');
}

}

JDK的工程师们用实际行动告诉我们,先抽象,再具体。这套路你学会了吗?

目录
相关文章
|
28天前
|
Java 大数据 API
Java Stream API:现代集合处理与函数式编程
Java Stream API:现代集合处理与函数式编程
185 100
|
28天前
|
Java API 数据处理
Java Stream API:现代集合处理新方式
Java Stream API:现代集合处理新方式
208 101
|
1月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
11天前
|
存储 算法 安全
Java集合框架:理解类型多样性与限制
总之,在 Java 题材中正确地应对多样化与约束条件要求开发人员深入理解面向对象原则、范式编程思想以及JVM工作机理等核心知识点。通过精心设计与周密规划能够有效地利用 Java 高级特征打造出既健壮又灵活易维护系统软件产品。
43 7
|
10天前
|
Java Go 开发工具
【Java】(9)抽象类、接口、内部的运用与作用分析,枚举类型的使用
抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体。抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接 口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类abstract static不能同时修饰一个方法。
125 0
|
19天前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
69 11
|
1月前
|
存储 Java Go
对比Java学习Go——函数、集合和OOP
Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了Go语言的简洁与高效设计。
|
2月前
|
存储 缓存 安全
Java集合框架(二):Set接口与哈希表原理
本文深入解析Java中Set集合的工作原理及其实现机制,涵盖HashSet、LinkedHashSet和TreeSet三大实现类。从Set接口的特性出发,对比List理解去重机制,并详解哈希表原理、hashCode与equals方法的作用。进一步剖析HashSet的底层HashMap实现、LinkedHashSet的双向链表维护顺序特性,以及TreeSet基于红黑树的排序功能。文章还包含性能对比、自定义对象去重、集合运算实战和线程安全方案,帮助读者全面掌握Set的应用与选择策略。
178 23
|
2月前
|
存储 缓存 安全
Java集合框架(三):Map体系与ConcurrentHashMap
本文深入解析Java中Map接口体系及其实现类,包括HashMap、ConcurrentHashMap等的工作原理与线程安全机制。内容涵盖哈希冲突解决、扩容策略、并发优化,以及不同Map实现的适用场景,助你掌握高并发编程核心技巧。
|
2月前
|
存储 NoSQL Java
Java Stream API:集合操作与并行处理
Stream API 是 Java 8 提供的集合处理工具,通过声明式编程简化数据操作。它支持链式调用、延迟执行和并行处理,能够高效实现过滤、转换、聚合等操作,提升代码可读性和性能。