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的工程师们用实际行动告诉我们,先抽象,再具体。这套路你学会了吗?

目录
相关文章
|
23小时前
|
安全 Java 调度
Java一分钟:多线程编程初步:Thread类与Runnable接口
【5月更文挑战第11天】本文介绍了Java中创建线程的两种方式:继承Thread类和实现Runnable接口,并讨论了多线程编程中的常见问题,如资源浪费、线程安全、死锁和优先级问题,提出了解决策略。示例展示了线程通信的生产者-消费者模型,强调理解和掌握线程操作对编写高效并发程序的重要性。
10 3
|
1天前
|
Java API
Java 接口
5月更文挑战第6天
|
1天前
|
存储 安全 Java
Java一分钟之-Map接口与HashMap详解
【5月更文挑战第10天】Java集合框架中的`Map`接口用于存储唯一键值对,而`HashMap`是其快速实现,基于哈希表支持高效查找、添加和删除。本文介绍了`Map`的核心方法,如`put`、`get`和`remove`,以及`HashMap`的特性:快速访问、无序和非线程安全。讨论了键的唯一性、`equals()`和`hashCode()`的正确实现以及线程安全问题。通过示例展示了基本操作和自定义键的使用,强调理解这些概念对编写健壮代码的重要性。
5 0
|
1天前
|
存储 安全 Java
Java一分钟之-集合框架进阶:Set接口与HashSet
【5月更文挑战第10天】本文介绍了Java集合框架中的`Set`接口和`HashSet`类。`Set`接口继承自`Collection`,特征是不允许重复元素,顺序不确定。`HashSet`是`Set`的实现,基于哈希表,提供快速添加、删除和查找操作,但无序且非线程安全。文章讨论了`HashSet`的特性、常见问题(如元素比较规则、非唯一性和线程安全性)以及如何避免这些问题,并提供了代码示例展示基本操作和自定义对象的使用。理解这些概念和注意事项能提升代码效率和可维护性。
9 0
|
1天前
|
存储 安全 算法
Java一分钟之-Java集合框架入门:List接口与ArrayList
【5月更文挑战第10天】本文介绍了Java集合框架中的`List`接口和`ArrayList`实现类。`List`是有序集合,支持元素重复并能按索引访问。核心方法包括添加、删除、获取和设置元素。`ArrayList`基于动态数组,提供高效随机访问和自动扩容,但非线程安全。文章讨论了三个常见问题:索引越界、遍历时修改集合和并发修改,并给出避免策略。通过示例代码展示了基本操作和安全遍历删除。理解并正确使用`List`和`ArrayList`能提升程序效率和稳定性。
7 0
|
2天前
|
Java
【JAVA进阶篇教学】第四篇:JDK8中函数式接口
【JAVA进阶篇教学】第四篇:JDK8中函数式接口
|
2天前
|
Java
Java一分钟之-抽象类与接口的应用场景
【5月更文挑战第9天】Java中,抽象类和接口用于实现多态和抽象。抽象类不能实例化,提供部分实现和定义模板;接口包含无实现的抽象方法,用于定义行为规范和解耦合。选择时,关注行为用接口,部分实现用抽象类。注意抽象类的`final`和`static`方法、接口冲突等问题,明确设计目标,适度抽象,遵循接口设计原则,以提高代码质量。
12 1
|
2天前
|
Java
Java一分钟之-多态性:理解重写与接口
【5月更文挑战第9天】本文介绍了Java中的多态性,主要通过方法重写和接口实现。重写允许子类根据实际类型执行不同实现,关键点包括方法签名相同、访问权限不降低以及final、static和abstract方法不可重写。接口是抽象类型,包含抽象方法,提供另一种多态性实现。常见问题包括混淆重载与重写、不理解动态绑定以及滥用接口。为避免问题,需明确重写目的、合理设计接口,并在使用多态时注意类型检查。多态性是提升代码质量和灵活性的关键。
9 1
|
23小时前
|
Java
Java一分钟:线程协作:wait(), notify(), notifyAll()
【5月更文挑战第11天】本文介绍了Java多线程编程中的`wait()`, `notify()`, `notifyAll()`方法,它们用于线程间通信和同步。这些方法在`synchronized`代码块中使用,控制线程执行和资源访问。文章讨论了常见问题,如死锁、未捕获异常、同步使用错误及通知错误,并提供了生产者-消费者模型的示例代码,强调理解并正确使用这些方法对实现线程协作的重要性。
9 3
|
23小时前
|
安全 算法 Java
Java一分钟:线程同步:synchronized关键字
【5月更文挑战第11天】Java中的`synchronized`关键字用于线程同步,防止竞态条件,确保数据一致性。本文介绍了其工作原理、常见问题及避免策略。同步方法和同步代码块是两种使用形式,需注意避免死锁、过度使用导致的性能影响以及理解锁的可重入性和升级降级机制。示例展示了同步方法和代码块的运用,以及如何避免死锁。正确使用`synchronized`是编写多线程安全代码的核心。
10 2