一、定义
二、实现
1、ArrayList
1.1、定义
ArrayList是通过列表方式存储数据的集合。它的底层数据结构是数组,且提供了动态调整数组容量的功能。ArrayList是线程不安全的,若需要使用线程安全的ArrayList,则可以使用Vector、Collection.synchronizedList()、CopyOrWriteArrayList。
Arrays.asList方法是构造一个固定不能修改的ArrayList,若需要可以修改的,可以使用new ArrayList(Arrays.asList(xxx))
1.2、特性
1.3、原理
(1)ArrayList的2个常用构造方法
A、无参构造方法
- public ArrayList()
- this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA(DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个空数组)
B、有参构造方法
- public ArrayList(int initialCapacity)
- if (initialCapacity > 0)
- this.elementData = new Object[initialCapacity]
- else if (initialCapacity == 0)
- this.elementData = EMPTY_ELEMENTDATA(EMPTY_ELEMENTDATA是一个空数组)
- else
- throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity)
- throw new IllegalArgumentException("Illegal Capacity: "+
- if (initialCapacity > 0)
(2)扩容机制
ArrayList的扩容机制主要是通过grow()方法。主要扩容逻辑是:若无参构建ArrayList,第一次扩容时按照10的1.5倍扩容,之后或者有参构造则都按照下面这种方式进行扩容:按照设置的容量*1.5倍进行扩容。
A、grow方法调用过程
- grow()
- return grow(size + 1)(size表示元素数组中包含的元素个数,size+1表示最小扩容容量)
- grow(int minCapacity)
- return elementData = Arrays.copyOf(elementData,newCapacity(minCapacity));(对旧数组进行扩容)
- newCapacity(int minCapacity)
- int oldCapacity = elementData.length;
- int newCapacity = oldCapacity + (oldCapacity >> 1)(对旧容量扩容1.5倍)
- if (newCapacity - minCapacity <= 0)(判断新容量是否小于等于最小扩容容量)
- if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)(无参构建且未扩容)
- return Math.max(DEFAULT_CAPACITY, minCapacity);(DEFAULT_CAPACITY为10,返回默认容量和最小容量之间的最大值)
- if (minCapacity < 0)(数据类型溢出)
- throw new OutOfMemoryError()
- return minCapacity(返回最小容量)
- if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)(无参构建且未扩容)
- return (newCapacity - MAX_ARRAY_SIZE <= 0)? newCapacity: hugeCapacity(minCapacity)(判断新容量是否小于等于数组最大容量,是则直接返回新容量)
- hugeCapacity(int minCapacity)(否,新容量大于最大容量则需要继续判断)
- if (minCapacity < 0) (数据类型溢出)
- throw new OutOfMemoryError()
- return (minCapacity > MAX_ARRAY_SIZE)? Integer.MAX_VALUE: MAX_ARRAY_SIZE(若最小扩容容量大于数组最大容量则返回Integer最大值,否则返回数组最大容量MAX_ARRAY_SIZE为 Integer.MAX_VALUE-8)
- if (minCapacity < 0) (数据类型溢出)
- hugeCapacity(int minCapacity)(否,新容量大于最大容量则需要继续判断)
- newCapacity(int minCapacity)
- return elementData = Arrays.copyOf(elementData,newCapacity(minCapacity));(对旧数组进行扩容)
- grow(int minCapacity)
- return grow(size + 1)(size表示元素数组中包含的元素个数,size+1表示最小扩容容量)
B、大体流程
- 确定最小扩容容量size+1
- 确定旧容量
- 计算新容量
- 判断新容量是否小于最小扩容量
- 是,判断数组容量是否为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
- 是,返回默认容量10
- 否,判断最小扩容量是否数据溢出
- 是,抛异常
- 否,返回最小扩容量
- 是,判断数组容量是否为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
- 判断新容量是否小于等于数组最大容量
- 是,返回新容量
- 否,判断最小扩容量是否数据溢出
- 是,抛异常
- 否,判断最小扩容量是否大于数组最大容量
- 是,返回Integer.MAX_VALUE-8
- 否,返回数组最大容量
(3)添加元素:add方法
A、add方法调用过程
- add(E e)
- add(e, elementData, size)
- add(E e, Object[] elementData, int s)
- if (s == elementData.length)
- elementData = grow()(进行扩容)
- elementData[s] = e(添加元素)
- size = s + 1
- if (s == elementData.length)
- add(E e, Object[] elementData, int s)
- return true
- add(e, elementData, size)
(4)删除元素
迭代删除元素的方式:
- 对源集合进行拷贝,利用迭代器方法对拷贝的列表进行删除操作;
- 对源集合进行拷贝,利用Stream方式对拷贝的列表进行删除操作;
删除集合中的重复元素: 利用Stream的distinct方法
2、LinkedList
LinkedList是List接口和Queue接口的实现,它是以双向链表的方式存储元素。与ArrayList相比,链表具体更高的灵活性,特别适用于频繁的插入和删除操作。但是,LinkedList由于每个节点都要维护前后元素的节点,所以占用的空间相对ArrayList会耗费空间,可这不是绝对的,由于ArrayList每次都会扩容到原理的1.5倍,所以它占用的空间也是不小的。
三、线程安全的List
1、线程安全的ArrayList
ArrayList是线程不安全的,在jdk1.0版本增加了Vector类,它是线程安全版的ArrayList。它是通过在读写方法上加上synchronized关键字来保证线程安全,且在扩容机制上,ArrayList扩容是容量的1.5倍,而Vector扩容是容量的2倍。但是,由于Vector是通过在读写方法上加上synchronized来保证线程安全的,所以性能较差,不建议使用Vector。也可以利用Collection.synchronizedList()构造一个线程安全的ArrayList。但是也是把所有方法加上synchronized关键字,性能也是较差,不建议使用。CopyOnWriteArrayList是线程安全版的ArrayList,它是的核心是复制一份再进行写入,且会在写数据加锁(可重入锁),读数据又不用加锁。在读多写少的场景可以大大提高数据读写性能。但是若是在读少写多的场景,由于每次写操作都要进行复制,性能和内存开销非常大,如果集合较大,非常容易出现内存溢出的问题。