一、List

在这里插入图片描述

List实现类的数据结构

  • Arraylist: Object数组
  • Vector: Object数组
  • LinkedList:
    双向链表(JDK1.6之前为循环链表,JDK1.7取消了循环)

Arraylist 与 LinkedList 的区别

  • 是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
  • 底层数据结构: Arraylist 底层使用的是 Object 数组;LinkedList 底层使用的是 双向链表 数据结构(JDK1.6之前为循环链表,JDK1.7取消了循环);
  • 插入和删除是否受元素位置的影响:
    ① ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e) 方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element) )时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
    ② LinkedList 采用链表存储,所以对于add(E e)方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置i插入和删除元素的话((add(int index, E element)) 时间复杂度近似为O(n)),因为需要先移动到指定位置再插入。
  • 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index) 方法)。
  • 内存空间占用: ArrayList的空间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)

List的遍历方式选择

  • 实现了 RandomAccess 接口的list,优先选择普通 for 循环 ,其次 foreach,
  • 未实现 RandomAccess接口的list,优先选择iterator遍历(foreach遍历底层也是通过iterator实现的)大size的数据,千万不要使用普通for循环

ArrayList与Vector的区别

  • Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。
  • Arraylist不是同步的,所以在不需要保证线程安全时建议使用Arraylist。

ArrayList扩容分析

成员变量
//Default initial capacity.
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.
 */
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.
 */
transient Object[] elementData;

//The size of the ArrayList (the number of elements it contains).
private int size; //基本数据类型,初始值为0

/**
 * The maximum size of array to allocate.
 * Some VMs reserve some header words in an array.
 * Attempts to allocate larger arrays may result in
 * OutOfMemoryError: Requested array size exceeds VM limit
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
空参构造方法
/**
 * Constructs an empty list with an initial capacity of ten.
 * 构建一个空的ArrayList对象
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

让成员变量elementData 指向DEFAULTCAPACITY_EMPTY_ELEMENTDATA,即一个空的Object[]数组,在我们没有添加元素之前数组为空,则说明在第一次添加元素的时候,就会发生扩容

add()方法
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

在执行ensureCapacityInternal(size + 1)之后,将数组:elementData[size]size位置赋值为当前要添加的元素,然后size++返回true,可见向ArrayList中添加元素,就是不断的在给Object[] elementData数组进行赋值操作,那么在赋值之前,首先就要保证的是这个数组有空位置来装下当前元素,这个过程就是通过执行ensureCapacityInternal()方法来保证的

ensureCapacityInternal()——保证内部容量
// 参数 minCapacity = size + 1
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity()——计算容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

首先判断elementData是不是DEFAULTCAPACITY_EMPTY_ELEMENTDATA,在空参的构造方法中让elementData指向了DEFAULTCAPACITY_EMPTY_ELEMENTDATA,所以这个判断为true,返回DEFAULT_CAPACITYminCapacity的较大值,此时添加元素时需要的最小容量minCapacitysize+1 = 1,而DEFAULT_CAPACITY = 10,所以返回10

ensureExplicitCapacity()方法——确保明确的容量
// minCapacity  = 10
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code 10 - 0 > 0 
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

modCount++后面解释,此时minCapacity - elementData.length(10 - 0 > 0)明显大于0,if 判断为true,执行grow()方法

grow()方法——扩容方法
//参数minCapacity = 10
private void grow(int minCapacity) {
    // overflow-conscious code,第一次为 0
    int oldCapacity = elementData.length;
    // 新容量 = 旧容量 + 旧容量*0.5
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    
    // 1、如果:新的容量 - 最小容量 < 0,则新容量 = 最小容量
    if (newCapacity - minCapacity < 0){
    	newCapacity = minCapacity;
    }
    
    // 2、如果新容量大于MAX_ARRAY_SIZE,则新容量 = hugeCapacity()的返回值
    if (newCapacity - MAX_ARRAY_SIZE > 0){
    	newCapacity = hugeCapacity(minCapacity);
    }
    
    // 3、将原数组复制到新数组中,原数组指向新的数组,容量为:newCapacity
    elementData = Arrays.copyOf(elementData, newCapacity);
}

(1) 当新容量大于最小容量时,即当向空的 List 集合中添加第一个元素的时候,oldCapacity = elementData.length = 0,执行
int newCapacity = oldCapacity + (oldCapacity >> 1); 即:newCapacity = 0,所以newCapacity - minCapacity = 0 - 10 < 0 成立,第一个 if 判断为true,新容量newCapacity = minCapacity = 10,有时候说的默认初始容量为10,是在添加的时候容量为10,在没有添加元素,刚创建出来的时候,初始容量为0 (Object[]空数组而已)

(2) 当扩容后的容量大于MAX_ARRAY_SIZE 的时候,执行hugeCapacity()方法

(3)Arrays.copyOf(elementData, newCapacity) 将elementData复制到一个新数组中,新数组长度为newCapacity(此时为10),原数组中的前10个(包含第10个,下标为9)拷贝到新数组中,不够10个,用0替代,然后让elementData 指向这个新的数组,就实现对原数组的拷贝和扩容,此时elementData 的长度就为10

当再次添加第2个,第3个…元素的时候,calculateCapacity()的第二个参数:minCapacity = 1+1,2+1,…,
此时 if 判断elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA为false,所以在添加第11个元素之前,该方法都返回的是传递进来的minCapacity

ensureExplicitCapacity()方法的参数就是calculateCapacity()的返回值,在添加第11个元素之前,方法中 if 判断minCapacity - elementData.length < 0总是成立,此时就不会执行grow()方法,直到添加到第11个元素时,minCapacity = 11,会再次执行grow()方法,此时新容量为旧容量的1.5倍为15,grow()中两个if判断都不为true,继续将旧数组拷贝到新数组中,新容量为15

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

在这个方法中比较了minCapacityMAX_ARRAY_SIZE的大小,如果前者大,则返回Integer.MAX_VALUE,否则返回MAX_ARRAY_SIZE,赋值给newCapacity

System.arraycopy()和Arrays.copyOf()
public static void main(String[] args) {
    int[] arr1 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int[] arr2 = new int[5];
    System.arraycopy(arr1,0,arr2,0,5);
    System.out.println(Arrays.toString(arr2));
    //[1, 2, 3, 4, 5]
}

System.arraycopy()参数说明:

  • 1、Object src : 原数组
  • 2、int srcPos : 从元数据的起始位置开始
  • 3、Object dest : 目标数组
  • 4、int destPos : 目标数组的开始起始位置
  • 5、int length : 要copy的数组的长度
public static void main(String[] args) {
    int[] arr1 = {1, 2, 3, 4, 5};
    int[] arr2 = Arrays.copyOf(arr1, 10);
    int[] arr3 = Arrays.copyOf(arr1, 3);
    //[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
    System.out.println(Arrays.toString(arr2));
    //[1, 2, 3]
    System.out.println(Arrays.toString(arr3));
}

ArrayList中的ensureCapacity()方法

public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;
    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}
public static void main(String[] args) {
    ArrayList<Object> list = new ArrayList<>();
    final int N = 10000000;
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < N; i++) {
        list.add(i);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("使用ensureCapacity方法前:"+(endTime - startTime));

    list = new ArrayList<>();
    long startTime1 = System.currentTimeMillis();
    list.ensureCapacity(N);
    for (int i = 0; i < N; i++) {
        list.add(i);
    }
    long endTime1 = System.currentTimeMillis();
    System.out.println("使用ensureCapacity方法后:"+(endTime1 - startTime1));
}

使用ensureCapacity方法前:2277
使用ensureCapacity方法后:375

Process finished with exit code 0

通过运行结果,我们可以很明显的看出向 ArrayList 添加大量元素之前最好先使用ensureCapacity 方法,以减少增量重新分配的次数

LinkedList

LinkedList是一个实现了List接口和Deque接口的双端链表。 LinkedList底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性; LinkedList不是线程安全的
私有静态内部类

private static class Node<E> {
    E item;//节点值
    Node<E> next;//后继节点
    Node<E> prev;//前驱节点

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值