List学习记录

数组

  1. 连续的内存空间
  2. 相同类型的数据
  3. 通过索引即数组下标快速定位元素
  4. 事先确定数组长度,不支持动态改变数组大小

链表

  1. 内存空间不连续动态分配内存
  2. 动态增加或删除元素
  3. 链表节点包含两部分内容,一个是元素的值,另一个是指向下一个元素的指针

List

元素有序、可重复

ArrayList(重点)

  • 线程不安全
  • 支持快速随机访问,即通过索引快速获取元素,对应get(int index)
  • 底层是Object[]数组
  • 长度不够需要扩容时,默认扩容为原来的1.5倍

JDK1.8之前时,无参构造时底层直接构建一个长度为10的Object[]数组。

JDK1.8及之后时,无参构造时底层的Object[]数组先初始化为{},当需要添加第一个元素时才初始化一个长度为10的Object[]数组,并进行元素添加。延迟了数组的创建,在一定程度上节省了内存

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

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    transient Object[] elementData;

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    ...

}

插入时的时间复杂度:

  • 最好情况,直接在尾部插入,时间复杂度为O(1);
  • 最坏情况,在头部插入,所有元素都需要向后移动一位,时间复杂度为O(n)。

删除时的时间复杂度:

  • 最好情况,直接删除末尾元素,时间复杂度为O(1);
  • 最坏情况,删除首部元素,剩余元素都需要向前移动一位,时间复杂度为O(n)。

查找时的时间复杂度:

  • 最好情况,首部元素即为所需元素,时间复杂度为O(1);
  • 最坏情况,末尾元素才是所需元素或者元素不存在,时间复杂度为O(n)。

ArrayList可以存储null

public static void main(String[] args) {
        ArrayList<String> array = new ArrayList<>();
        array.add("hhh");
        array.add(null);
        //输出[hhh, null]
        System.out.println(Arrays.toString(array.toArray()));
}

ArrayList的扩容机制

//将指定的元素追加到此列表的末尾
public boolean add(E e) {
        //添加元素前先调用ensureCapacityInternal方法
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
}

private void ensureCapacityInternal(int minCapacity) {
        //进一步调用
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //判断数组是否为{},如果是则返回默认容量10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //如果不是,则返回minCapacity
        return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        //如果calculateCapacity返回的容量比数组elementData的长度大,则调用grow方法
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
}

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //右移一位相当于除以2,扩大为原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果扩容后的容量比传入的参数小,则将传入的参数进行赋值
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果扩容后的容量比默认最大容量还要大,则进一步处理
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //数组复制
        elementData = Arrays.copyOf(elementData, newCapacity);
}
  • 当add第1个元素时,elementData是个空数组,此时内部调用ensureCapacityInternal(0+1),然后调用calculateCapacity(elementData, 1),返回默认容量10,接着调用ensureExplicitCapacity(10),由于10-elementData.length > 0,则调用grow(10),确定数组容量为10,使用Arrays.copyOf方法进行数组复制。
  • 当add第2个元素时,此时内部调用ensureCapacityInternal(1+1),然后调用calculateCapacity(elementData, 2),返回2,由于第一次添加元素时elementData扩容为10了,所以2-elementData.length > 0不成立,不会调用grow(2)。
  • 当add第3、4、... 、10个元素时,同理,都不会调用grow方法。
  • 当add第11个元素时,此时内部调用ensureCapacityInternal(10+1),然后调用calculateCapacity(elementData, 11),返回11,由于elementData的容量为10,所以11-elementData.length > 0成立,则调用grow(11),在原容量的基础上数组容量扩容了1.5倍,即newCapacity=15,然后使用Arrays.copyOf方法进行数组复制。

LinkedList

  • 线程不安全
  • 底层是双向链表(JDK1.6之前是双向循环链表,JDK1.7之后取消了循环)
  • 两个指针,一个指向头结点,一个指向尾结点

插入时的时间复杂度:

  • 直接在头部或尾部插入,时间复杂度为O(1);
  • 在指定位置插入,需要先移动到该位置,再进行插入操作,时间复杂度为O(n)。

删除时的时间复杂度:

  • 直接删除头结点或尾结点,时间复杂度为O(1);
  • 在指定位置插入,需要先移动到该位置,再进行删除操作,时间复杂度为O(n)。

查找时的时间复杂度:

  • 查找的是头结点或尾结点对应的值,时间复杂度为O(1);
  • 查找的是其他结点的值或者所查找的元素不存在,需要移动进行比较,时间复杂度为O(n)。

Vector

  • 线程安全
  • 底层是Object[]数组,容量不够时扩容为原来的2倍
  • 基本不使用

ArrayList和数组的区别

  1. ArrayList能动态扩容,数组不能扩容;
  2. ArrayList创建时既可以自定义长度也可以不定义,而数组需要自定义长度;
  3. ArrayList可以使用泛型确定存储的数据类型,数组不是;
  4. ArrayList只存储对象,不存储基本数据类型,数组既可以存储基本数据类型也可以存储对象;
  5. ArrayList有一些方法可实现插入、删除等操作,而数组没有。

ArrayList和LinkedList的区别

  1. ArrayList和LinkedList都是线程不安全的;
  2. ArrayList底层是Object[]数组,LinkedList底层是双向链表
  3. ArrayList支持快速随机访问,而LinkedList不支持;
  4. ArrayList的空间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间浪费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值