JDK源码学习(四)---ArrayList

一、简介

1、ArrayList继承关系

2、继承关系解析

1、顶层Iterable接口,可迭代

2、Collection继承自Iterable,并添加了size()、isEmpty()、contains等方法。

3、List继承了Collection接口,针对List的特性加入了get、set、sort、sublist等方法。

4、AbstractCollection中即有自己实现了方法体的一些函数,也有抽象方法约束子类去实现。(主要依赖于设计模式,因为具体的集合子类根据自身的特性不同,实现的方法也会不一样,如果定义成抽象方法,那么子类需要去实现)

/**
     * {@inheritDoc}
     *
     * <p>This implementation always throws an
     * {@code UnsupportedOperationException}.
     *
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws ClassCastException            {@inheritDoc}
     * @throws NullPointerException          {@inheritDoc}
     * @throws IllegalArgumentException      {@inheritDoc}
     * @throws IndexOutOfBoundsException     {@inheritDoc}
     */
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }

5、AbstractList中设计到迭代器

两个主要的函数:

public E next() {
            checkForComodification();
            try {
                int i = cursor;//先把游标付给i
                E next = get(i);//拿到游标后的第一个元素 next
                lastRet = i;//将 lastRet 置为 i ,表示最新一次访问的元素位置
                cursor = i + 1;//游标后移一位
                return next;//返回数值
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        public void remove() {
            if (lastRet < 0)//判断 lastRet 是不是 -1,如果是的话说明这个元素被一个 remove 方法调用了,不能连续调用两次 remove,会报错
                throw new IllegalStateException();
            checkForComodification();//如果不是 -1,紧接着判断并发操作

            try {
                AbstractList.this.remove(lastRet);//调用子类实现的 remove 方法删除最新访问的元素,重新更新游标
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;//上次访问的元素位置置为 -1
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

Ltr 看下来只是简单实现了 Iterator 中的 next 和 remove 方法,ListLtr 中实现了previous 方法以及set方法:

//previous 方法中,返回迭代器游标之前的一个元素,并同时设定游标值和最后一次访问元素的坐标值。当调用 previous 函数时,可能出现最后一次访问元素坐标与游标重合,其它情况下则不会出现
public E previous() {
            checkForComodification();
            try {
                int i = cursor - 1;
                E previous = get(i);
                lastRet = cursor = i;
                return previous;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        public int nextIndex() {
            return cursor;
        }

        public int previousIndex() {
            return cursor-1;
        }
//Set 方法则是将列表最后一次访问的元素替换为 e
        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.set(lastRet, e);
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

equals方法的实现:

public boolean equals(Object o) {
        if (o == this)
            return true;//如果是本身直接返回 true
        if (!(o instanceof List))
            return false;//instanve of 判断类型部署 List 的话直接 false

        ListIterator<E> e1 = listIterator();
        ListIterator e2 = ((List) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;//否则取双发迭代器,依次比较元素,如果其中一个为 null 又或者 二者不相等 就 false
        }
        return !(e1.hasNext() || e2.hasNext());
    }

二、代码解析

1、构造函数

public ArrayList()//构造了一个空数组
public ArrayList(int initialCapacity)//构造了一个指定长度的数组
public ArrayList(Collection<? extends E> c)//构造了一个指定Collection的数组

2、元素添加

add(E e) 和add(int index, E element)

add(E e)是在ArrayList插入到最后,add(int index, E element)是在指定位置index插入element。如果index不是在ArrayList,就涉及到数组元素的移动。

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

逻辑主要牵扯到扩容:

 /**
     * 默认初始容量.
     */
    private static final int DEFAULT_CAPACITY = 10;
    /**
     * 用于空实例的空数组实例。如 ArrayList list = new ArrayList<>() list就等于{}.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 
    public void ensureCapacity(int minCapacity) {
        //判断需要扩容的数组是否为空实例(空数组)如果为不为空,则变量等于0.为空则变量等于数组默认容量 10
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            ? 0
            : DEFAULT_CAPACITY;
        //如果需要扩容的量大于定义的变量。则进一步调用以下方法。
        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        //需要扩容的量大于原数组的长度,则进一步调用方法。(其实这里就本文章对ensureCapacity()
        //分析来说,我觉得这个方法完全写在上面的方法中,没必要再单独再写个方法做判断。)
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    //要分配的数组的最大大小
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
     /**
     * 真正扩容的地方
     */
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;  //原数组的长度
        int newCapacity = oldCapacity + (oldCapacity >> 1);  //原数组的长度+原数组的长度/2
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity; // 系统给予的扩容策略所扩的容量<用户给的扩容量,则改用用户指定扩容量
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            // 如果需要扩容的量大于了本类中定义的最大扩容限制,则扩容到 int 类型最大长度
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);// 扩容,其实调用的的是数组的复制方法
    }
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) 
            throw new OutOfMemoryError();
        // 如若需要扩容的量大于了最大限制,则扩容量改为 int 最大限制量:2147483647。否则为本类中所限制长度:2147483647-8
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

3、元素移除

removeremoveRange 等方法的实现也差不多,例如 remove 中需要压缩列表,将最后一个位置置为 null ,让 gc 清理,因为涉及到修改的话,modCount 会 ++ ;fastRemove 为私有方法,跳过了越界检查并且不返回删除的元素值

4、元素排序

sort方法可以对ArrayList中的元素进行排序,排序使用CopyOnWrite的方法。

5、遍历

1、普通for遍历

2、迭代器iterator

 public Iterator<E> iterator() {
        return new Itr();
    }
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
Iterator<String> it = list.iterator();
while(it.hasNext()){
    String str = it.next();
    System.out.print(str+" ");
}

3、迭代器的变种 forEach

ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
for(String str : list){
    System.out.print(str + " ");
}

4、迭代器 ListIterator

public ListIterator<E> listIterator(int index) {
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException("Index: "+index);
        return new ListItr(index);
    }
ArrayList<String> list = new ArrayList<>();
 list.add("a");
 list.add("b");
 list.add("c");
 ListIterator<String> listIt = list.listIterator();
 
 //向后遍历
 while(listIt.hasNext()){
    System.out.print(listIt.next()+" ");//a b c
}
//向后前遍历,此时由于上面进行了向后遍历,游标已经指向了最后一个元素,所以此处向前遍历能有值
while(listIt.hasPrevious()){
    System.out.print(listIt.previous()+" ");//c b a
}

三、总结

1、是一个动态数组,容量可以自动扩容,不适合频繁增加元素的场景。

2、随机查询效率高,但是增删的复杂度较高。

3、非线程安全。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值