与Collection接口相比,List接口新增了如下方法:
- add(int index,E element):在列表指定位置插入指定元素
- addAll(int index,Collection<? extends E> c):在指定位置添加集合元素
- get(int index):获取指定位置的元素
- indexOf(Object o):获取元素的第一个位置
- isEmpty():判断列表中是否有元素
- lastIndexOf(Object o):获取指定元素的最后一个位置
- listIterator():获取列表元素的列表迭代器
- listIterator(int index):从指定位置开始获取列表迭代器
- remove(int index):移除指定位置的元素
- set(int index,E element):用指定元素替换指定位置的元素
- subList(int fromIdex,int toIndex):获取fromIndex到toIndex的元素列表
List接口实现类 有ArrayList、LinkedList、Vector和抽象类AbstractList,其中ArrayList和LinkedList较为常用
ArrayList:
自动变长机制:
当调用add方法时,会调用ensureCapacityInternal方法进行扩容,每次扩容为原来大小的1.5倍,但是当第一次添加元素或者列表中元素个数小于10的话,列表容量默认为10,扩容之后,将指定元素复制到新的element数组中
扩容原理:
根据当前数组的大小,判断是否小于默认值10,如果大于,则需要扩容至当前数组大小的1.5倍,重新将新扩容的数组数据copy只当前elementData,最后将传入的元素赋值给size++位置
//1.继承AbstractList和实现List
//2.底层是一个变长的数组,基本上等同于Vector
//3.对writeObject()和readObject()方法实现了同步
//4.是线程不安全的,多线程环境下要通过外部的同步策略后使用
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
}
优点:
操作读取操作效率高,基于数组实现的,可以为null值,可以允许重复元素,有序,异步。
缺点:
由于它是由动态数组实现的,不适合频繁的对元素的插入和删除操作,因为每次插入和删除都需要移动数组中的元素。
增加方法:
- colone():返回ArrayList实例的浅表副本
- ensureCapacity(int minCapacity):增加ArrayList实例的容量。
(改变list的容量会降低效率,改变list容量的原理是常见一个比之前更大容量的list,将原list中的数据copy到新的list,在删除原有的list) - removeRange(int fromIndex,int toIndex):批量移除列表中的元素
- trimToSize():将列表容量调整至当前大小。
部分方法原理:
add(E e)
当调用add插入时,首先要调用ensureCapacityInternal(size + 1)方法,上文提到这个方法是进行自动扩容的,效率低重点也就是在这个扩容上了,每次新增都要对现有的数组进行一次1.5倍的扩大,数组间值的copy等,最后等扩容完毕,有空间位置了,将数组size+1的位置放入元素e,实现新增。
add(int index, E element)
给指定的index赋值element,首先rangeCheckForAdd(index)要对加入的index的位置进行check,index非法则抛出越界异常,throw new IndexOutOfBoundsException(outOfBoundsMsg(index));其他方法也有用到如:addAll(int index, Collection<? extends E> c),然后同理也是需要扩容ensureCapacityInternal(size + 1),完成扩容后,将数组间的数据进行一个copy,这主要是为了保证数组数据完整,空出一个index位置,不像add中的elementData[size++]就完事了,最后将指定位置的index元素赋值,个数+1。
remove(int index)
在删除index位置的元素时,要先调用rangeCheck(index)进行index的check,index要超过当前个数,则判定越界,抛出异常,throw new IndexOutOfBoundsException(outOfBoundsMsg(index));其他方法也有用到如:get(int index),set(int index, E element)等后面删除重点在于计算删除的index是末尾还是中间位置,末尾直接–,然后置空完事,如果是中间位置,那就要进行一个数组间的copy,重新组合数组数据了,性能就在这里得到了消耗。
get(int index)
获取指定index的元素,首先调用rangeCheck(index)进行index的check,通过后直接获取数组的下标index获取数据,就这么简单明了,没有任何多余操作,效率之高,相对LinkedList的遍历获取,较为方便高效。
LinkedList:
索引查找get(x):
所有根据索引的查找操作都是按照双向链表的需要执行的,根据索引从前或从后开始搜索,并且从最靠近索引的一端开始。例如一个LindedList有5个元素,如果调用了get(2)方法,LinkedList将会从头开始搜索;如果调用get(4)方法,那么LinkedList将会从后向前搜索。这样做的目的可以提升查找效率,因为在LinkedList内部有一个Node(int index)方法,它会判断从头或者从后开始查找比较快。
内部结构-链表Node:
LinkedList的每个节点都是一个Node类型的实例,每个Node中都包含着当前元素,当前元素的前节点以及后节点,这样就实现了节点之间的环环相扣,跟链条一样的具备双向的链表结构。
//是一个双向链表
//实现了writeObject方法和readObject方法的同步
//线程不安全的,多线程环境下要通过外部的同步策略后使用
//可以存放任何元素,包括null
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}
优点:
LinkedList由双链表实现,增删由于不需要移动底层数组数据,其底层是链表实现的,只需要修改链表节点指针,对元素的插入和删除效率较高。
缺点:
遍历效率较低。HashMap和双链表也有关系。
新增方法:
其中有部分方法实现的功能是一样的,例如add方法和offer方法实现的功能是一样的,只是返回的类型不同,add无返回类型,offer返回boolean。remove和poll方法的不同之处是:当列表为空时,poll返回null,而remove会抛出异常。
- addFirst(E e):将指定元素添加到刘表开头
- addLast(E e):将指定元素添加到列表末尾
- descendingIterator():以逆向顺序返回列表的迭代器
- element():获取但不移除列表的第一个元素
- getFirst():返回列表的第一个元素
- getLast():返回列表的最后一个元素
- offerFirst(E e):在列表开头插入指定元素
- offerLast(E e):在列表尾部插入指定元素
- peekFirst():获取但不移除列表的第一个元素
- peekLast():获取但不移除列表的最后一个元素
- pollFirst():获取并移除列表的最后一个元素
- pollLast():获取并移除列表的最后一个元素
- pop():从列表所表示的堆栈弹出一个元素
- push(E e);将元素推入列表表示的堆栈
- removeFirst():移除并返回列表的第一个元素
- removeLast():移除并返回列表的最后一个元素
- removeFirstOccurrence(E e):从列表中移除第一次出现的指定元素
- removeLastOccurrence(E e):从列表中移除最后一次出现的指定元素
部分方法原理:
add(E e)
将元素append至list的末尾,具体过程是:新建一个Node节点,其中将后面的那个节点last作为新节点的前置节点,后节点为null;将这个新Node节点作为整个list的后节点,如果之前的后节点l为null,将新建的Node作为list的前节点,否则,list的后节点指针指向新建Node,最后size+1,当前llist操作数modCount+1。在add一个新元素时,LinkedList所关心的重要数据,一共两个变量,一个first,一个last,这大大提升了插入时的效率,且默认是追加至末尾,保证了顺序,当然指定位置插入除外。
add(int index, E element)
remove(int index)
删除指定index的元素,删除之前要调用checkElementIndex(index)去check一下index是否存在元素,如果不存在抛出throw new IndexOutOfBoundsException(outOfBoundsMsg(index));越界错误,同样这个check方法也是很多方法用到的,如:get(int index),set(int index, E element)等。