ArrayList与LinkedList区别源码分析

常用数据结构

List,Set,Map的区别

Set是最简单的一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。 Set接口主要实现了两个实现类:HashSet: HashSet类按照哈希算法来存取集合中的对象,存取速度比较快

List的特征是其元素以线性方式存储,集合中可以存放重复对象。

Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 Map没有继承于Collection接口 从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。

数组和链表的区别

  • 数组:
    处理一组数据类型相同的数据。但是不允许动态定义数组的大小,即在使用数组之前必须确定数组的大小。这样数组中的有些空间可能不被使用,从而造成内存空间的浪费。当数据增加时,可能超出原先定义的元素个数,造成数组越界。数组插入删除时需要移动其他数据项。但是查询方便。
    数组从栈中分配空间,对于程序员方便快速,数组无需初始化,因为数组元素在内存的栈区,系统自动申请空间。但是自由度小。数组元素在内存中连续

  • 链表:
    链表动态的进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。链表插入和删除时,只需要改变个别元素之间的关系,这大大提高了链表的删除和插入的速度。查询时需要从前往后遍历。
    链表从堆中分配空间,自由度大,但是申请管理比较麻烦,链表的结点元素在内存的堆区,每个元素须手动申请空间。链表在内存中不一定连续。

ArrayList

简介

这里写图片描述

/**
     * Default initial capacity.
     * ArrayList 默认的数组容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     *  用于空实例的共享空数组实例
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     * 另一个共享空数组实例,用的不多,用于区别上面的EMPTY_ELEMENTDATA
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     * ArrayList底层的容器  
     */
    // Android-note: Also accessed from java.util.Collections
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     * 当前存放了多少个元素   并非数组大小
     */
    private int size;

ArrayList是基于动态数组的数据结构。实现了list接口,是以数组的方式实现的。所谓动态数组是这样实现的,如果没有指定数组的大小,则申请默认大小为10 的数组,当元素个数增加,数据无法存储时,系统会另外申请一个长度为当前长度的1.5倍的数组,然后把之前的数据拷贝到新建的数组中。
数组的特征是可以使用索引的方式来快速定位对象的位置。适合读取数据。

ArrayList源码分析

  • 添加
/**
* 添加指定元素到末尾
*/
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

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

        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
  //扩容
 private void grow(int minCapacity) {
        // overflow-conscious code
        //1. 记录之前的数组长度
        int oldCapacity = elementData.length;
        //2. 新数组的大小=老数组大小+老数组大小的一半
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //3. 判断上面的扩容之后的大小newCapacity是否够装minCapacity个元素
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
              //4.判断新数组容量是否大于最大值
    //如果新数组容量比最大值(Integer.MAX_VALUE - 8)还大,那么交给hugeCapacity()去处理,该抛异常则抛异常
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //5. 复制数组,注意,这里是浅复制
        elementData = Arrays.copyOf(elementData, newCapacity);

private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

思路:
1.首先判断添加一个元素是否会导致数组溢出
2.判断是否溢出:如果原数组为空,那么第一次添加数组时会给数组一个默认大小10,接着判断是否溢出,如果溢出则去扩容,扩容规则:新数组是原数组大小的1.5倍,最后通过Arrays.copyOf()去浅复制
3.添加元素到末尾

  • 获取
/**
 * 返回指定位置处元素
*/
 public E get(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        return (E) elementData[index];
    }

思路:通过下标获得数组中的元素

  • 移除
public E remove(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        modCount++;
        E oldValue = (E) elementData[index];
//    复制的长度
        int numMoved = size - index - 1;
        if (numMoved > 0)
        //数组之间的复制 
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
public static native void arraycopy (Object src,//源数组
                             int srcPos,//源数组要复制的起始位置
                             Object dest,//目的数组
                          
  • 14
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值