美文网首页
ArrayList的学习笔记

ArrayList的学习笔记

作者: Hyutao | 来源:发表于2019-02-22 13:11 被阅读0次

    ArrayList的继承和接口实现

    1、ArrayList的简单介绍

    1. ArrayList是一个动态数组队列,它可自动扩容,每次扩容大小为原来的1.5倍,初始的默认值为10
    
    private static final int DEFAULT_CAPACITY = 10;
    
    
    1. ArrayList底层是由Object数组构成

    2. ArrayList是线程不安全的,如果想使用线程安全的Arraylist可使用Collections.synchronizedList(List l)函数

    3. ArrayList对删除和添加(非两端)操作是具有昂贵时间花费的

    4. ArrayList对访问元素操作所花费的时间是常数的

    Tips

    ArrayList建议使用情况

    ①适用于需要多次访问数据

    ②经常在末端添加元素

    2、ArrayList的构造函数

    ArrayList提供三种构造函数

    ①public ArrayList(int initialCapacity)

    
    public ArrayList(int initialCapacity) {
    
        if (initialCapacity > 0) {
    
            this.elementData = new Object[initialCapacity];
    
        } else if (initialCapacity == 0) {
    
            this.elementData = EMPTY_ELEMENTDATA;
    
        } else {
    
            throw new IllegalArgumentException("Illegal Capacity: "+
    
                                              initialCapacity);
    
        }
    
    }
    
    //声明的变量
    
    transient Object[] elementData; // non-private to simplify nested class access
    
    private static final Object[] EMPTY_ELEMENTDATA = {};  //默认为空
    
    

    首先判断输入是初始值是否大于0,如果大于就直接开辟一个初始值大小的数组

    如果等于0就直接把EMPTY_ELEMENTDATA的数组的地址赋予初始的elementData

    ②public ArrayList()

    
    public ArrayList() {
    
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    
    }
    
    transient Object[] elementData; // non-private to simplify nested class access
    
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    

    当不填写初始值时,会直接把DEFAULTCAPACITY_EMPTY_ELEMENTDATA的地址赋予初始的elementData

    但是上面说初始值是10,但是这里并没有出现10,这是为什么呢?

    这就不得不介绍另一个函数 确定内部容量函数

    
    private void ensureCapacityInternal(int minCapacity) {
    
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    
            }
    
            ensureExplicitCapacity(minCapacity);
    
        }
    
    

    扩容函数是Arraylist的基础,上面很很幸免的写到,如果elementData的地址为DEFAULTCAPACITY_EMPTY_ELEMENTDATA的地址,就直接使用DEFAULT_CAPACITY(10)的容量

    这里就很好解释了为什么ArrayList的初始默认值为10

    ③public ArrayList(Collection<? extends E> c)

    
    public ArrayList(Collection<? extends E> c) {
    
        elementData = c.toArray();
    
        if ((size = elementData.length) != 0) {
    
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
    
            if (elementData.getClass() != Object[].class)
    
                elementData = Arrays.copyOf(elementData, size, Object[].class);
    
        } else {
    
            // replace with empty array.
    
            this.elementData = EMPTY_ELEMENTDATA;
    
        }
    
    }
    
    

    创建一个包含collection的ArrayList,这个构造方法不是很常用,着重介绍一下后面的通配符

    Collection<? extends E> c

    ①必须是Collection类型的

    ② ? extends E , ? 代表着任意的,extends很好理解就是继承,这个E代表着和AyyayList相同的数据类型

    大家回忆一下这个类一开始

    
    public class ArrayList<E> extends AbstractList<E>
    
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
    

    这个这个E实际上是和ArrayList<E>是相同的含义,必须是相同的数据类型

    3、ArrayList的类继承关系

    image

    首先根据继承关系,一层一层推到出Arraylist的继承情况

    
    public class ArrayList<E> extends AbstractList<E>
    
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
    

    ①首先继承了AbstractList这个抽象类

    ②实现了List,RandomAccess,Cloneable,和序列化接口(java.io.Serializable)

    Tips

    1、什么是RandomAccess

    答:RandomAccess接口实际上是一个标志性接口,你实现了这个接口代表你具有随机访问数据的能力(既通过数组下标直接访问数据)由此可知LinkedList肯定不会实现这个接口,因为它无法通过下标访问

    
    public interface RandomAccess {
    
        //RandomAccess,java源码里面什么都没有
    
    }
    
    

    2、Cloneable接口的作用

    答:Cloneable接口也是一个标记性接口,实现这个接口代表着你可被clone,能够使用Object.clone()方法

    
    public interface Cloneable {
    
        //和上面的随机访问接口一样,不具有任何代码
    
    }
    
    

    3、什么是序列化

    答:

    把对象转换为字节序列的过程称为对象的序列化

    把字节序列恢复为对象的过程称为对象的反序列化

    对象的序列化主要有两种用途:

    1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;

    2) 在网络上传送对象的字节序列。

    通俗的来说就是ArrayList内的数据可以保存在你的硬盘上

    4、什么是List接口

    我们打开List接口的源码

    
    public interface List<E> extends Collection<E>
    
    

    一上来展示的就是List接口继承与Collection接口,那我们再翻到Collection接口

    
    public interface Collection<E> extends Iterable<E>
    
    

    这时我们发现,怎么还有接口,那么继续往上翻

    
    public interface Iterable<T>
    
    

    这下终于没有接口了,舒服了。。。。

    这时我们发现 Iterable迭代器接口是ArrayList的顶级父类,这三个接口继承到底让List接口获得了那些功能呢

    ①Iterable接口

    
    public interface Iterator<E> {
    
        /**
    
        * Returns an iterator over elements of type {@code T}.
    
        *
    
        * @return an Iterator.
    
        */
    
        Iterator<T> iterator();
    
    
    
        // 1.8新增了两个方法,暂不讨论
    
    }
    
    

    上面的代码说明表示会返回一个Iterator类型的变量

    Iterator是迭代器接口,这就表明获得了一个迭代器

    所以说,Iterable使List接口获得了迭代器功能

    ②Collection接口

    
    public interface Collection<E> extends Iterable<E> {
    
        // Query Operations
    
    
    
        int size();
    
        boolean isEmpty();
    
        boolean contains(Object o);
    
    
    
        Iterator<E> iterator();
    
        <T> T[] toArray(T[] a);
    
        boolean add(E e);
    
        boolean remove(Object o);
    
        boolean containsAll(Collection<?> c);
    
        boolean addAll(Collection<? extends E> c);
    
        boolean removeAll(Collection<?> c);
    
        boolean retainAll(Collection<?> c);
    
    
    
        void clear();
    
    
    
        boolean equals(Object o);
    
    
    
        int hashCode();
    
    }
    
    

    Collection是List和set接口的父类,他提供了操作数据的基本方法名称

    由此可知Collection使得List获得了基本操作方法的名称

    ③List接口

    List接口继承了上述两个接口的所有特性,但是List接口代表着的是有序的Collection接口

    即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素

    5、什么是AbstractList抽象类

    我们首先打开AbstractList抽象类的源码

    
    public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
    
    

    到这里我们发现了一个我们熟悉的List接口,但是还没结束,继续翻

    
    public abstract class AbstractCollection<E> implements Collection<E>
    
    

    到这里呢,我们又见到了我们熟悉的Collection接口

    那么问题来了,我们为什么要创建一个AbstractCollection抽象类呢?

    在此呢,我们需要复习几个概念

    ①当一个类实现某一个接口时,必须要实现这个接口的所有方法

    ②当一个类继承某一个类时,可以重写也可以不重写父类的方法

    ③抽象类是具有抽象方法的类,有抽象方法的类一定是抽象类,没有抽象方法的类也可能是抽象类

    ④抽象类无法创建实例,必须被继承后重写抽象方法

    答:当我们需要实现Collection接口时,我们必须重写Collection接口中的全部方法,而AbstractCollection则帮我们实习了大多数的方法,我们只需要重写其中的几个方法,即可完成Collection接口的使用

    未实现的方法为

    
    public abstract Iterator<E> iterator();
    
    public abstract int size();
    
    

    同理

    
    public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
    
    

    在AbstractList抽象类中,实现了List的大多数方法,只剩下几个必须要子类实现的函数没有实现

    比如add(), set(),remove(),还有size()

    
    public void add(int index, E element) {
    
        throw new UnsupportedOperationException();
    
    }
    
    

    如不重写add()方法就会抛出异常

    那么到目前为止,我们发现了AbstractList抽象类继承了AbstractCollection抽象类

    ①AbstractCollection抽象类完成了Collection接口的基本方法但还保留了抽象方法等待子类去重写实现

    ②AbstractList抽象类进一步完善了AbstractCollection抽象类的方法,使得其变成了表结构,也就是说数据开始有序起来,在几个关键函数中AbstractList强制子类重写方法不然会爆出异常

    相关文章

      网友评论

          本文标题:ArrayList的学习笔记

          本文链接:https://www.haomeiwen.com/subject/zhxeyqtx.html