java.util.AbstractCollection学习笔记

前言

由今天开始,正式开始学习java.util包中的collection接口,及其相关类,并在该博客账号中进行更新,尽量每天一更,如果在博客中存在错误,欢迎各位在留言区进行指证,相互交流,共同进步。

今天将从基本的AbstractCollection类开始学习,选择这个类的原因是:这个类提供了collection的实现类应该具有的基本方法,具有一定的普适性,可以从大局上了解collection实现类的主要功能。

继承结构

这里写图片描述

构造函数

在看代码的时候发现一件很奇怪的事情,抽象类AbstractCollection居然有protected构造函数,代码如下:

protected AbstractCollection() {
}

经过查资料得出如下结论:

  1. 首先,抽象类不能实例化是绝对正确的,因此抽象类中并不能包含public的构造方法;
  2. 抽象类protected构造方法会被隐性调用,因此并不一定在其子类的构造方法中显示调用super(),虽然对于AbstractCollection而言是建议这么做;
  3. 抽象类的protected构造方法可以用于初始化类中的某些属性,避免一场信息的出现出现;

为了验证以上三点,写出测试代码如下:

 public abstract class AbstractClass {
	    private int m;
	    protected AbstractClass() {
	    }
	    protected AbstractClass(int n) {
	        m = n;
	    }
	    public int getM() {
	        return m;
	    }
	    public void setM(int m) {
	        this.m = m;
	    }
}


public class TestExtendClass extends AbstractClass {
	public TestExtendClass() {
		super(12);
	}

	public static final void main(String[] args) {
	   TestExtendClass testExtendClass = new TestExtendClass();
	   System.out.println("hello world " + testExtendClass.getM());
	}
}

hello world 12

当TestExtendClass并没有显式调用super()时,程序仍能够正常进行,调用super(12)时,可以执行AbstractClass的构造函数,从而对其中的属性进行初始化操作。

抽象函数

在AbstractCollection的注释文档中提到:

To implement an unmodifiable collection, the programmer needs only to extend this class and provide implementations for the iterator and 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 <tt>add</tt> method (which otherwise throws an UnsupportedOperationException), and the iterator returned by the iterator method must additionally implement its remove method.

由此可以看出,AbstractCollection将其实现类分成两种,一种为只读集合,另一种为可修改集合。

在只读集合中,只需要实现AbstractCollection中的iterator函数和size函数即可,其它的函数可以维持不变(在对性能没要求的前提下),这保证了实现类只需要少量的工作,便可以将集合的功能基本实现。

而对于可修改集合,AbstractCollection要求不仅需要实现其中的两个抽象方法,还需要实现add方法,并保证iterator函数返回的迭代器中实现了remove方法。

对于非抽象算法,AbstractCollection的建议为:如果有更加高效的实现方法,子类可以将其重写(override),建议原文如下:

The documentation for each non-abstract method in this class describes its implementation in detail.  Each of these methods may be overridden if the collection being implemented admits a more efficient implementation.

非抽象函数

boolean isEmpty():判断集合是否为空集合,基本算法为判断size()函数的返回值是否大于0;

boolean contains(Object o):通过迭代器遍历链表,判断集合中是否包含某一元素,对于特殊null对象,采取的是==运算符,对于普通对象则调用equals()函数判断是否相等;

public Object[] toArray():这个函数的地位比较特殊,扮演者将Collection转化为数组的角色,因此将进行详细分析,其源代码与调用的相关代码如下:

public Object[] toArray() {
        Object[] r = new Object[size()];
        Iterator<E> it = iterator();
        for (int i = 0; i < r.length; i++) {
            if (! it.hasNext())
                return Arrays.copyOf(r, i);
            r[i] = it.next();
        }
        return it.hasNext() ? finishToArray(r, it) : r;
    }

private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
        int i = r.length;
        while (it.hasNext()) {
            int cap = r.length;
            if (i == cap) {
                int newCap = cap + (cap >> 1) + 1;
                // overflow-conscious code
                if (newCap - MAX_ARRAY_SIZE > 0)
                    newCap = hugeCapacity(cap + 1);
                r = Arrays.copyOf(r, newCap);
            }
            r[i++] = (T)it.next();
        }
        // trim if overallocated
        return (i == r.length) ? r : Arrays.copyOf(r, i);
    }
    
private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError
                ("Required array size too large");
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

在函数toArray()中,首先根据collection当前的大小初始化一个数组,并根据集合大小设定期望的元素个数。而后使用迭代器依次遍历集合中的元素,如果发现集合的元素自第i个起均被删除,则直接返回r中的前i个元素。
如果发现在开始转化后,集合中插入了新的元素,则会进入:扩容+复制的循环。其中扩容部分将数组容量扩展到原来的1.5倍。当复制过程中,数组容量再次填满时,则又进行扩容。最后返回数组中所有有效的元素。
注意:该算法的复制并不是100%准确的,其只能保证其数组中元素的个数与集合迭代器遍历的元素个数相同,且顺序相同,而不是保证该数组中元素与集合元素相同。

<T> T[] toArray(T[] a)将集合复制到指定的数组中。

public <T> T[] toArray(T[] a) {
        // Estimate size of array; be prepared to see more or fewer elements
        int size = size();
        T[] r = a.length >= size ? a :
                  (T[])java.lang.reflect.Array
                  .newInstance(a.getClass().getComponentType(), size);
        Iterator<E> it = iterator();

        for (int i = 0; i < r.length; i++) {
            if (! it.hasNext()) { // fewer elements than expected
                if (a == r) {
	                //数组a的长度大于集合中期望的元素个数,此时a与r等价
                    r[i] = null; // null-terminate
                } else if (a.length < i) {
	                // 数组a的长度小于集合中期望的元素个数
	                // 并且数组a的长度小于集合中当前已经转化的元素个数
                    return Arrays.copyOf(r, i);
                } else {
	                //数组a的长度小于集合中期望的元素个数
	                //并且数组a的长度大于等于当前已经转化的元素个数,则将r中的有效元素复制到a中
                    System.arraycopy(r, 0, a, 0, i);
                    if (a.length > i) {
                        a[i] = null;
                    }
                }
                return a;
            }
            r[i] = (T)it.next();
        }
        // more elements than expected
        return it.hasNext() ? finishToArray(r, it) : r;
    }

该函数要求用户指定数组地址。但是要注意:当数组长度大于集合大小时,该函数将基于原数组进行修改,而当数组长度小于集合大小时,该函数将根据集合大小申请一个新的数组。因此保险做法为,当调用该方法是,将原数组的地址覆盖。即:a=toArray(a)。为了验证该猜想,编写测试代码如下:


public class App {

    public static void main(String[] args) {
        Long[] a = {1L, 2L, 3L, 4L};

        List<Long> list = new ArrayList<Long>();
        list.add(4L);
        list.add(5L);

        System.out.println(a);
        for(int i = 0 ; i < a.length ; i++) {
            System.out.print(a[i] + " ");
        }
        System.out.println();

        list.toArray(a);
        System.out.println(a);
        for(int i = 0 ; i < a.length ; i++) {
            System.out.print(a[i] + " ");
        }
        System.out.println();

        Long[] b = {1L};
        System.out.println(b);
        for(int i = 0 ; i < b.length ; i++) {
            System.out.print(b[i] + " ");
        }
        System.out.println();

        list.toArray(b);
        System.out.println(b);
        for(int i = 0 ; i < b.length ; i++) {
            System.out.print(b[i] + " ");
        }
        System.out.println();

        b=list.toArray(b);
        System.out.println(b);
        for(int i = 0 ; i < b.length ; i++) {
            System.out.print(b[i] + " ");
        }

        System.out.println();
    }
}

输出结果如下:
[Ljava.lang.Long;@b81eda8
1 2 3 4 
[Ljava.lang.Long;@b81eda8
4 5 null 4 
[Ljava.lang.Long;@68de145
1 
[Ljava.lang.Long;@68de145
1 
[Ljava.lang.Long;@27fa135a
4 5 

boolean add(E e)向集合中添加一个元素。集合默认为只读的,因此缺省实现为抛出一个UnsupportedOperationException。如果需要实现一个可读写集合,则需要重写该方法。

boolean remove(Object o)删除集合中的特定元素。集合通过迭代器在集合中查找需要删除的对象,如果存在,则调用迭代器的remove方法删除该对象,并返回true,否则返回false。该方法要求迭代器必须实现remove方法,否则将抛出UnsupportedOperationException

boolean containsAll(Collection<?> c)判断该集合中是否包含集合c中的所有元素,是返回true,否则返回false;

boolean addAll(Collection<? extends E> c) 将集合c中的所有元素添加到目标集合中。该方法要求重写add方法。如果所有元素均添加成功,返回true,否则将抛出异常信息。

boolean removeAll(Collection<?> c)从目标集合中删除集合c中包含的所有元素,要求与remove方法类似;

boolean retainAll(Collection<?> c)要求集合c非空。将目标集合与集合c取交集,并删除目标集合中的其他元素;

void clear()通过迭代器遍历目标集合中的每个元素,并调用迭代器的remove方法删除元素;

String toString()将集合装化为String,格式为:[value1, value2, value3, value4]

posted @ 2016-11-01 12:50  leipeng2016  阅读(893)  评论(0编辑  收藏  举报