Java基础之有容乃大篇(AbstractCollection超类)

超类间的关系看图说话:

Java基础之有容乃大篇(AbstractCollection超类)_第1张图片    还是辣张图,四个超类AbstractCollection、 AbstractList、 AbstractSet 、AbstractSequentialList,关于AbstractCollection , AbstractList , AbstrcatSet见名知意,这三个超类分别实现了Collection,List,Set接口,对这些接口的规范进行了部分实现,还有一些未实现的被定义为抽象方法,使子类自己去实现 。 AbstractSequentialList继承了AbstractList这个类提供了一个基本的List接口实现,为实现 序列访问的数据储存结构的提供了所需要的最小化的接口实现。AbstractList采取的是随机访问的数据就想数组可以根据下标访问,这里AbstractSequentialList采用的是在迭代器的基础上实现的get、set、add和remove方法。用一句白话来讲 链表不能用下标访问,我们面向接口编程必须要有一个合理的迭代访问的超类线性表供子类去继承他,也是LinkedList的最直接的超类。

AbstractCollection

    用最原始的分析方式,

        1. 有啥没啥 ,

        2. 啥是啥 ,                      其中定义了基类会用到的一些方法,还有子类必须去实现的方法

        3. 啥能干啥,                    它所实现的方法,子类如果没有更复杂的操作可以使用,不需要重写

        4. 啥和啥有啥关系。          AbstractCollection是所有Collection系列的容器的基类

    AbstractCollection是Collection的实现类,它实现了Collection的部分方法,也留下了一些方法让子类去实现,那么它具体实现了哪些方法又没有实现哪些呢?

    没实现的方法代码及描述:

    public abstract Iterator iterator();            //迭代器方法
    public abstract int size();                        //size返回集合当前元素个数的方法

    实现了没写内容不能用的方法:

    public boolean add(E e) {          //这个方法有方法体,但是不能用因为没有实现需要子类重写
        throw new UnsupportedOperationException();
    }                                 //子类可以根据需要去重写这个方法,add方法在数组和链表以及(set是map结构)的实现方式是不一样的
        当然这个方法不能用,凡是关于add的就都不能用了。

    实现的方法代码及描述:

        1.判空的方法isEmpty()

    public boolean isEmpty() {                //看集合当前是否为空  为空返回true 
        return size() == 0;                    //不为空返回false
    }

        2.集合查找元素

    public boolean contains(Object o) {        //查看集合是否有Objedt o这个元素
        Iterator it = iterator();            //首先需要一个当前集合的迭代器
        if (o==null) {                            //o为空看集合里有没有空值
            while (it.hasNext())
                if (it.next()==null)
                    return true;
        } else {                            //o不为空看看集合里有没有o值
            while (it.hasNext())
                if (o.equals(it.next()))
                    return true;
        }
        return false;
    }

        3.集合转数组

    public Object[] toArray() {
        // Estimate size of array; be prepared to see more or fewer elements
        Object[] r = new Object[size()];    //new一个集合大小的object数组
        Iterator it = iterator();        //获取一个迭代器
        for (int i = 0; i < r.length; i++) {    //循环把object数组的每一个元素从0 - n 插入迭代器不断遍历的值
            if (! it.hasNext()) // fewer elements than expected
                return Arrays.copyOf(r, i);
            r[i] = it.next();
        }
        return it.hasNext() ? finishToArray(r, it) : r;
    }

        4.集合转数组

    
    /**
     *这种方法是将list转化为你所需要的类型的数组,当然我们用的时候会转化为与list内容相同的类型。     *
     *比如我List中装的全部是String类型,我希望的也是转变成string类型,我们就会给其一个条件告诉他转化成string数组
     *其实从道理上来讲,上面的toArray不方便,返回了Object我们还得转过来,这里我们规定一个类型返回的就是固定类型的数组方便
     */
    @SuppressWarnings("unchecked")
    public  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 it = iterator();
                                                //我们要把 r 数组的每一个元素填充进来,外层循环
         for (int i = 0; i < r.length; i++) {
            if (! it.hasNext()) {     //如果迭代器没有下一个元素了
                if (a == r) {         //如果 数组 a地址 等于 数组 r地址   
                    r[i] = null;     //这最后一个元素就不用填了
                } else if (a.length < i) {    //如果a的长度还小于i
                    return Arrays.copyOf(r, i); //返回一个 把r数组复制给一个i长度的数组
                } else {//a既不小于i,又不等于r
                    System.arraycopy(r, 0, a, 0, i); //我们就把r从0开始复制i个元素  复制到a从0开始粘贴i个元素     
                    if (a.length > i) {     //a长度大于i    为什么这里和场面要看a因为a的长度不知道是多少但是集合就这么几个元素
                        a[i] = null;        //a【i】等于0     也就是我只要稳稳当当的把集合里面的元素复制给a就好了
                    }
                }
                return a;//返回a数组
            }
            r[i] = (T)it.next(); //有下一个元素就把元素扔到r里面
        }
        //如果集合还有更多的元素也就是说上面开辟完了空间之后集合中元素多了超过了r能接收的范围,那么我们需要去调用finishToArray(r,it)
        return it.hasNext() ? finishToArray(r, it) : r;
    }

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;  //对数组的长度有一定的限制分配较大的数组可能导致OutOfMemory

    @SuppressWarnings("unchecked")
    private static  T[] finishToArray(T[] r, Iterator it) {    //既然容器中元素辣么多那么我们就要对r进行扩容了
        int i = r.length;                //记录当前满了的大小我们扩完容好继续添加元素
        while (it.hasNext()) {            //添加元素
            int cap = r.length;
            if (i == cap) {                //长度不够了就扩容
                int newCap = cap + (cap >> 1) + 1;    //阔容容量  原容量+原容量/2+1 也就是1.5倍
                // overflow-conscious code
                if (newCap - MAX_ARRAY_SIZE > 0)        //扩完了超过了规定的最大长度那么我们把当前需要最小的容量传入到
                    newCap = hugeCapacity(cap + 1);        //hugeCapacity函数里面,大于最大表示范围就用Integer的最大表示范围,
                r = Arrays.copyOf(r, newCap);            //小于最大表示范围就用我们规定的最大表示范围,也就是integer最大范围-8
            }
            r[i++] = (T)it.next();
        }
        // trim if overallocated            //r的长度在扩容之后也不确定,那么我们不管怎么样都要用i长的数组,所以有了之下的判断
        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;
    }

        5.   remove方法,从集合中移开这个元素( 注意 只是移开了元素而不是删掉了元素。)

   public boolean remove(Object o) {            //移开的意思是  把鸡蛋从一个篮子里面拿到另一个地方,鸡蛋还在  具体拿开之后
        Iterator it = iterator();            //要不要吃了,这个不管,我这个方法只是拿开了
        if (o==null) {                            //为空移除
            while (it.hasNext()) {                //遍历移除
                if (it.next()==null) {
                    it.remove();                    //Iterator接口中的移除方法
                    return true;
                }
            }
        } else {                        //不为空移除
            while (it.hasNext()) {
                if (o.equals(it.next())) {
                    it.remove();
                    return true;
                }
            }
        }
        return false;
    }
        6.集合里有没有这个集合里的全部元素    判断当前集合是否包含参数集合
public boolean containsAll(Collection c) {
        for (Object e : c)            
            if (!contains(e))            //对参数集合挨个调用contains
                return false;
        return true;
    }
        7.把参数集合加入到当前集合中  没有实现add方法前 不能用的
    public boolean addAll(Collection c) {
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }
        8.凡是c中的元素我在原有集合中全部删了
    public boolean removeAll(Collection c) {
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator it = iterator();
        while (it.hasNext()) {     //对原集合中的元素挨个查再没在c中 ,在的话调用remove
            if (c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    }
        9.取原集合和参数集合的交集放入原集合中
    public boolean retainAll(Collection c) {
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator it = iterator();
        while (it.hasNext()) {                //
            if (!c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    }
        10.把集合中的元素都移出集合

    public void clear() {
        Iterator it = iterator();
        while (it.hasNext()) {
            it.next();
            it.remove();
        }
    }
        11.toString方法,很关键,我们的所用的集合能输出出集合中的内容,这个实现起到了关键性的作用
    public String toString() {
        Iterator 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(' ');
        }
    }                            //把集合中的所有数据转变成了字符串,采用的是线程不安全的效率高的StringBuilder()类来拼接。

总结一下:

        对于抽象方法,我们必须去重写,iterator , size ,非抽象方法,add不用的话可以不写,对于别的方法,如果有更好的实现方式或者是不适用的子类可以进行重写。总之AbstractCollection就实现了这些方法。

        至于为什么没有把add实现或者是规定为抽象方法,这样做是合理的,我的回答如下:

            1.因为add实现的方式有很多不能确定子类是哪种数据结构(链表,数组,Map(数组+链表)),所以我们没法去显示的实现它,就算间接的用某种方式实现,也显得太过冗余而没有必要。

            2.为什么没规定为抽象方法,是因为如果子类不想用add子类就是一个删改查的集合。那么我们不用还必须要去实现岂不是很矛盾。

       

                                                                                                            大神若指点,在下愿改之。如有困惑,可谈论一二。

     

你可能感兴趣的:(JAVA基础知识)