ArrayList 与 LinkedList

1.List

  • List 集合:元素有序、且可重复
  • List是Collection的子接口,ArrayList 与 LinkedList是 List 的实现类

在这里插入图片描述

2.ArrayList

2.1 ArrayList 特点

在这里插入图片描述

// Arraylist 声明
public class ArrayList<E> extends AbstractList<E>
 implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • 线程不安全
  • 底层使用 动态Object 数组存储
  • ArrayList 实现 RandomAccess 接口,支持快速随机访问:不需要遍历就可以通过下标(索引)直接访问到内存地址。
  • ArrayList 实现了 Cloneable 接口,重写了 Object 类的 clone() 方法。表明 ArrayList 是支持拷贝的。
  • ArrayList 实现了 Serializable 接口,支持序列化
  • 查询、更改速度快,增加、删除速度慢 (查改直接可以根据地址进行访问,增删会涉及到大量数据的移动)
    在这里插入图片描述

2.2 ArrayList源码分析(JDK7)

  • 构造器
//提供了3个构造器
1.public ArrayList()  //构造一个默认初始容量为 10 的空列表
2.public ArrayList(int initialCapacity) //构造一个指定初始容量的空列表
3.public ArrayList(Collection<? extends E> c)//构造一个包含指定 collection 的元素的列表

在这里插入图片描述

  • 数组扩容 (判断→扩容1.5倍→copy)
    (1) 向数组中添加元素时,先检查当前元素的个数+1 是否会超出当前数组的长度
public boolean add(E e)
{
	ensureCapacityInternal(size + 1); 
	// 此处的size是已经存在的元素的长度
	// 因为add() 只能添加一个元素,所以使用size+1 判断当前容量是否够用
		
	elementData[size++] = e;//将要添加的元素e放置在当前数组末尾
	return true;
}

(2)当前元素的个数+1 会超出当前数组的长度则进行扩容

private void ensureCapacityInternal(int minCapacity)
{
	modCount++;
	// elementData.length是 底层数组的长度
	if (minCapacity - elementData.length > 0) //若差值大于0 则表明底层数组长度不够,需要扩容
		grow(minCapacity); //扩容函数
}

(3) 判断扩容1.5倍后是否满足要求。
若满足扩容要求则将旧数组copy到扩容后大小的新数组中
若不满足扩容要求则直接将新容量设置为要扩容的大小,其他情况再做相应的处理。最后进行copy

private void grow(int minCapacity) 
{
	int oldCapacity = elementData.length; // elementData.length 是当前底层数组个数
		
	// >>表示右移运算符(实际相当于÷2),则新的容量是旧容量的1.5倍
	int newCapacity = oldCapacity + (oldCapacity >> 1);
	
	//单纯的add()函数并不会触发以下两个if,但是其他需要调用扩容方法的函数可能触发以下两个if	
	if (newCapacity - minCapacity < 0) //若扩容1.5倍后还是不够则直接将新容量设置为minCapacity
		newCapacity = minCapacity;
	if (newCapacity - MAX_ARRAY_SIZE > 0)//若扩容1.5被后大于MAX_ARRAY_SIZE,就再进行其他处理
			newCapacity = hugeCapacity(minCapacity);
	elementData = Arrays.copyOf(elementData, newCapacity);//将旧数组中的内容copy到新数组中		
}

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

注:为减少数组扩容次数,提升性能。当我们可预知要保存的元素的数量时,创建对象时就指定其容量

2.3 ArrayList源码分析(JDK8)

  • JDK8与JDK7不同的是:JDK8在创建空参ArrayList时不会预先创建大小未10 的object 数组 ( 而是创建空参数组 )。新增元素时才初始化object数组大小为10,其余操作与JDK7一致
private static final int DEFAULT_CAPACITY = 10;
DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

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

public boolean add(E e)
{
    ensureCapacityInternal(size + 1);  
    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);
     }
    return minCapacity;
}

3 LinkedList

3.1 LinkedList特点

在这里插入图片描述

// LinkedList 声明
public class LinkedList<E> extends AbstractSequentialList<E>
 implements List<E>, Deque<E>, Cloneable, java.io.Serializable
  • 底层使用双向链表存储
  • LinkedList 是继承自 AbstractSequentialList 的双向链表,因此它也可以被当作堆栈、队列或双端队列进行操作
  • LinkedList 实现了 Cloneable 接口,这表明 LinkedList 是支持拷贝的
  • LinkedList 实现了 Serializable 接口,这表明 LinkedList 是支持序列化的
    LinkedList 还实现了 Serializable 接口,这表明 LinkedList 是支持序列化的。
    *查询、更改速度慢,增加、删除速度快 (查改某个节点需要先找到其前一个节点,增删只需要处理其相邻的节点)
    在这里插入图片描述

3.2 LinkedList源码分析(JDK8)

Node节点结构
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;
        }
}

//在尾部插入一个节点: add
public boolean add(E e) 
{
    linkLast(e);
    return true;
}

//生成新节点 并插入到 链表尾部, 更新 last/first 节点。
void linkLast(E e) 
{ 
    final Node<E> l = last; //记录原尾部节点
    final Node<E> newNode = new Node<>(l, e, null);//以原尾部节点为新节点的前置节点
    last = newNode;//更新尾部节点
    if (l == null)//若原链表为空链表,需要额外更新头结点
        first = newNode;
    else//否则更新原尾节点的后置节点为现在的尾节点(新节点)
        l.next = newNode;
    size++;//修改size
    modCount++;//修改modCount
}

//get()方法
public E get(int index) 
{
    checkElementIndex(index);//判断是否越界 [0,size)
    return node(index).item; //调用node()方法 取出 Node节点,
}

//根据index 查询出Node,
Node<E> node(int index) 
{
    //通过下标获取某个node 的时候,(增、查 ),会根据index处于前半段还是后半段 进行一个折半,以提升查询效率
    if (index < (size >> 1))
    {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

4.总结

  • ArrayList 底层是 动态object 数组,LinkedList底层是双向链表
  • 对于随机访问( get( )、set( )方法 ) ArrayList要优于LinkedList。对于频繁增加、删除 LinkedList 优于ArrayList。
  • ArrayList 数组扩容 (判断→扩容1.5倍→copy)
  • 对于末尾添加元素,ArrayList与LinkedList 开销一致
  • ArrayList 调用 get( )时直接返回index位置上的元素
    LinkedList 调用 get( )时 需要通过for循环进行二分查找
  • ArrayList 想要在指定位置插入或删除元素时,主要耗时的是System.arraycopy动作,会移动index后面所有的元素;LinkedList主耗时的是要先通过for循环找到index,然后直接插入或删除
  • ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间
  • ArrayList、LinkedList 都实现了 Cloneable、Serializable 接口
  • ArrayList 继承于 AbstractList。LinkedList 继承于AbstractSequentialList
  • ArrayList 实现 RandomAccess 接口,支持快速随机访问
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值