Java集合框架学习笔记(一):ArrayList和LinkedList

381 阅读4分钟
原文链接: www.geekmuseo.com

1、ArrayList

ArrayList是一种基于数组的可随机读取的List。

1.1、重要的变量

DEFAULT_CAPACITY:默认容量为10

elementData:一个Object数组,用来存储数据

size:数组当前容量


1.2、数组容量

初始化时,如果不传容量,则用默认容量10来初始化数组,如果传入容量则用传入的数进行初始化。如果初始化传入的是另一个列表,则用这个列表的容量来初始化数组,然后将列表数据复制到这个数组中。每次容量不够的时候,按照1.5倍扩容,超过Integer最大值则抛异常。


1.3、get/set

public E get(int index)

public E set(int index, E element)

这两个方法都可以指定数组位置,get方法直接利用数组下标取值,算法复杂度为O(1),set方法会把指定下标到数组末尾的数据整体向后移动一个位置,然后在空出来的位置插入数据,算法复杂度为O(n)。具体见下图:

1.4、add

public boolean add(E e)

public void add(int index, E element)

第一个add方法表示在末尾插入数据,第二个add方法表示在指定位置插入数据,第二个方法也会发生数组内部数据的整体移动,性能会受影响。


1.5、remove

public E remove(int index)

public boolean remove(Object o)

第一个方法是将指定位置的数据删除,被删除数据之后的数据会整体往前移动一个位置第二个方法则是遍历整个数组,然后比较两个值是否相同,相同则进行删除,然后被删除数据之后的数据会整体往前移动一个位置。在ArrayList上做remove操作是一个性能较差的操作。


1.6、总结

ArrayList是基于数组的List,它的随机读取很快,向末尾插入数据也很快,随机插入和删除数据比较慢。


2、LinkedList

LinkedList是基于双向链表的List

2.1、数据结构

LinkedList底层使用Node作为数据存储的结构,它包含一个数据对象的引用,以及前驱、后继节点的引用,具体如下图:

LinkedList保存一个head节点作为头节点,它指向第一个节点,保存一个tail节点,它指向最后一个节点。插入数据有头部插入和尾部插入两种,头部插入则是通过操作head引用来实现,尾部插入通过操作tail引用实现。


2.2、add操作

public boolean add(E e)

public void add(int index, E element)

public void addFirst(E e)

public void addLast(E e)

第一个add方法使用尾部插入,算法复杂度为O(1)第二个add方法在指定位置插入,它首先需要找到指定位置的节点,然后改变前驱后继的引用进行插入,最坏情况下有O(n)复杂度。第三个方法是在头部插入,第四个方法是在尾部插入,都比较简单。


2.3、remove操作

public E remove()

public E remove(int index)

public boolean remove(Object o)

public E removeFirst()

public E removeLast()

第一个方法会删除最后一个节点第二个方法删除指定节点,它首先遍历链表,找到节点,然后进行删除,算法复杂度为O(n)第三个方法删除指定数据,它首先遍历链表,找到节点,然后删除,算法复杂度为O(n)第四个方法删头节点指向的节点,第五个方法删除尾节点


2.4、get操作

public E get(int index)

public E getFirst()

public E getLast()

第一个操作会遍历链表,找到指定位置节点,算法复杂度为O(n)第二个操作返回头结点指向的节点第三个操作返回尾节点指向的节点


2.5、set操作

public E set(int index, E element)

首先遍历链表,找到指定位置,然后进行数据替换


2.6、总结

LinkedList的插入、删除操作算法复杂度为O(1),但是查询操作需要遍历链表,算法复杂度为O(N),另外LinkedList需要额外承担Node节点生成和回收的开销。因此,在读多写少的情况下使用ArrayList、在写多读少的情况下使用LinkedList。