Java(JDK1.9)集合框架归纳总结——Collection接口的第二层子接口和抽象类

本文深入剖析了Java集合框架中的核心接口与抽象类,包括List、Set、Queue及Deque等,详细介绍了它们之间的继承关系、关键特性和实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:该系列文章总结自JDK1.9源码的文档注释和源码,我对源码的部分中文注释已经同步到Github:https://github.com/Dodozhou/JDK

Collection的第二层接口和抽象类

Collection第二层常用接口、类继承体系:
Level2
Collection继承体系的第二层,即List、AbstractCollection、Set和Queue的常用直接子类或接口如图所示。一一说明如下:

  • AbstractList:实现自List接口和AbstractCollection抽象类。该类是List接口的框架性实现,用来减小实现list接口的难度。该类的所有直接实现都是以“random access”的方式访问元素(简而言之就是可以通过下标访问元素,如数组)。对于需要以链表方法是组织元素的集合,应该优先考虑继承AbstractSequentialList。注:AbstractSequentialList也是一个抽象类,且是该类的子类。
  • AbstractSet:实现自Set接口和AbstractCollection抽象类。该类是Set接口的一个框架性实现,目的是为了减小实现Set接口的难度。除了一些独有的特性外(如Set不允许插入重复元素),继承该类等同于继承AbstractCollection。
  • AbstractQueue:实现自Queue接口和AbstractCollection抽象类。该类是Queue接口的一个框架性实现,目的是为了减小实现Queue接口的难度。该类不允许放入null值。
  • SortedSet:SortedSet能保持元素整体有序。元素根据自身的原始顺序或指定的Comparator进行大小比较。迭代器遍历的顺序使升序的。该类为利用有序性额外提供几个方法。
  • Deque:实现自Queue接口。该接口定义了一种支持两端进行删除和插入的线性集合,即双端队列。Deque是“double ended queue”的缩写,读作“deck”。

AbstractList

AbstractCollection中定义了一些Collection通用操作。该类在这些基本操作的基础上做了什么工作呢?

  • 对List接口的通过下标插入和删除元素的4种方法进行了抽象:public abstract E get(int index);public E set(int index, E element);public void add(int index, E element);public E remove(int index); get方法是抽象方法,其他3种直接在内部抛出了UnsupportedOperationException。
  • 重写了继承自AbstractCollection的add(E e)、clear()、Iterator()方法。
  • 对List接口的indexOf(Object o)、lastIndexOf(Object o)、addAll(int index, Collection<? extends E> c)等方法进行了初步实现:
public int indexOf(Object o) {
        ListIterator<E> it = listIterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return it.previousIndex();
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return it.previousIndex();
        }
        return -1;
    }
  • 定义了私有迭代器内部类private class Itr implements Iterator<E>、private class ListItr extends Itr implements ListIterator<E>、static final class RandomAccessSpliterator<E> implements Spliterator<E>并实现了其内部功能。
  • 定义并实现了一些新的方法,如removeRange(int fromIndex, int toIndex):
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();
        }
    }
  • 定义并实现了SubList类:private static class SubList<E> extends AbstractList<E> ,该列可以用来获取List的一段视图。注意,对视图的操作都是调用原List的方法,因此这些操作的结果都会映射到原List中。并且,使用了modeCount进行快速失败检测。

AbstractSet

该类是Set接口的一个框架性实现,目的是为了减小实现Set接口的难度。除了一些独有的特性外(如Set不允许插入重复元素),继承该类等同于继承AbstractCollection。注意,该类没有重写任何AbstractCollection的方法,它仅仅添加了equals和hashCode的实现。
它做了什么工作呢?

重写了来自Object的equals(Object)和HashCode()方法,定义了一个集合整体的equals和hashCode逻辑:
public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Set))
            return false;
        Collection<?> c = (Collection<?>) o;
        if (c.size() != size())
            return false;
        try {
            return containsAll(c);
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }

重写了equals方法,用来判断传入的对象是否和本集合“等同”。当传入的对象o也是一个set,从且两个集合的大小相等,一个集合中的所有元素也存在于另一个集合中(注意,没有要求顺序也相同,因为Set是不保证顺序的)。

public int hashCode() {
        int h = 0;
        Iterator<E> i = iterator();
        while (i.hasNext()) {
            E obj = i.next();
            if (obj != null)
                h += obj.hashCode();
        }
        return h;
    }

集合的hashCode即使所有元素HashCode的和这和AbstractList的hashCode不同,因为set无序,所以不用考虑顺序。而AbsctractList的所有实现都是有序的,它的它的hashCode方法将元素的位置也考虑了进去,具体为:

for (E e : this)
    hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
重写了AbstractCollection的removeAll(Collection<?> c)方法
public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;

        if (size() > c.size()) {
            for (Object e : c)
                modified |= remove(e);
        } else {
            for (Iterator<?> i = iterator(); i.hasNext(); ) {
                if (c.contains(i.next())) {
                    i.remove();
                    modified = true;
                }
            }
        }
        return modified;
    }

哪个集合较小,就使用哪个集合的迭代器进行遍历。如果本Set较小,那么使用本集合的迭代器进行遍历,并使用iterator.remove方法进行删除.如果传入的集合较小,那么使用传入集合的迭代器对其进行遍历,调用本集合的remove方法进行删除。

### AbstractQueue
实现自Queue接口和AbstractCollection抽象类。该类是Queue接口的一个框架性实现,目的是为了减小实现Queue接口的难度。该类不允许null值。扩展该类的队列实现必须最低限度地定义一个offer方法,它不允许插入空元素,以及相应的peek、poll、size和iterator方法。该类做了哪些工作呢?

  • 重写了继承自AbstractCollection的add(E)、clear()和addAll(Collection
public boolean add(E e) {
        if (offer(e))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }
    public void clear() {
        while (poll() != null)
            ;
    }
    public boolean addAll(Collection<? extends E> c) {
        if (c == null)
            throw new NullPointerException();
        if (c == this)
            throw new IllegalArgumentException();
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }
  • 实现了Queue接口的remove()和element()方法
public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }
    public E element() {
        E x = peek();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

SortedSet 接口

SortedSet能保持元素整体有序。元素根据自身的原始顺序或指定的Comparator进行大小比较。迭代器遍历的顺序使升序的。该类为利用有序性额外提供几个方法。
所有被插入Set中的元素都必须实现Comparable接口,或者通过构造器传入一个Comparator。对任意的两个元素e1、e2,都需要能通过e1.compareTo(e2)或者comparator.compare(e1, e2)进行比较。

所有通用的有序set实现类都必须拥有4个标准构造器:

  1. 一个无参构造器用于创建一个空的set,根据元素的自然顺序进行排序。
  2. 只含有一个Comparator参数的构造器。用于创建一个空的set,根据指定的比较器进行排序
  3. 只含有一个Collection类型元素的构造器。创建一个包含此元素的set,根据这个元素的自然顺序进行排序
  4. 只包含一个SortedSet类型元素的构造器。创建一个新的和传入set相同的元素和顺序的SortedSet。

由于接口中不能包含构造器,因此上面的要求并不能强制要求。
有几个方法可以返回指定范围的子集合。这个范围是半开半闭的(包含开始点,不包含结束点)。如果你需要指定一个闭区间,可以按照以下方式:

SortedSet<String> sub = s.subSet(low, high+"\0");

同样的方法可以用来构造一个全开范围:

SortedSet<String> sub = s.subSet(low+"\0", high);

该接口新定义了5个方法:

  • subSet(E,E):获取指定范围(前闭后开)的子集合
  • headSet(E toElement):获取从头部到toElement范围(不包括)内的子集合
  • tailSet(E fromElement):从formElement(包括)到尾部的子集合
  • first():获取第一个元素
  • last():获取最后一个元素

Deque 双端队列

该接口定义了一种支持两端进行删除和插入的线性集合。Deque是“double ended queue”的缩写。绝大多数的Deque实现对元素的数量没有固定的限制。此接口同时支持容量受限和不限的双端队列。同Queue一样,Deque的所有的操作都有两种形式:失败了抛异常或者返回一个标志。但是该类多了一组从另一端进行操作的方法。它的方法列表如下:

Summary of Deque methods
First Element (Head) Last Element (Tail)
Throws exceptionSpecial valueThrows exceptionSpecial value
InsertaddFirst(e)offerFirst(e)addLast(e)offerLast(e)
Remove removeFirst()pollFirst()removeLast()pollLast()
ExaminegetFirst()peekFirst()getLast()peekLast()

当Deque被当做Queue使用时,元素从Deque的尾部插入,头部移除,相应的方法对应如下:

Comparison of Queue and Deque methods
{@code Queue} Method Equivalent {@code Deque} Method
add(e)addLast(e)
offer(e)offerLast(e)
remove() removeFirst()
poll()pollFirst()
element()getFirst()
peek()peekFirst()

Deque也可以当做栈使用,当Deque被当做Stack使用时,元素从Deque的头部插入,头部移除,相应的方法对应如下:

Comparison of Stack and Deque methods
Stack Method Equivalent {@code Deque} Method
push(e) addFirst(e)
pop()removeFirst()
peek()peekFirst()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值