赞
踩
List是一个有序的集合,和set不同的是,List允许存储项的值为空,也允许存储相等值的存储项。
List继承了 Collection的接口,因此包括 Collection提供的各种方法,初次之外还扩展了自己的方法,如下图所示。
ArrayList是一个数组实现的列表,底层是数组,由于数据是存入数组中的,所以它的特点也和数组一样,查询很快,但是中间部分的插入和删除很慢。
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8683452581122892189L; /** * Default initial capacity.默认长度 */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. */ private static final Object[] 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 == EMPTY_ELEMENTDATA will be expanded to * DEFAULT_CAPACITY when the first element is added. */ //从这里可以看到,ArrayList的底层是由数组实现的,并且默认数组的默认大小是10 private transient Object[] elementData; /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size;
ArrayList的构造函数
ArrayList有2个构造函数,一个是默认无参的,一个是有参的(数组大小)
如果我们可以预估数组大小,最好使用使用有参构造函数。因为如果使用无参的构造函数,会首先把EMPTY_ELEMENTDATA(默认是10)赋值给elementData
然后根据插入个数于当前数组size比较,不停调用Arrays.copyOf()方法,扩展数组大小造成性能浪费。
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
add操作
//从尾部插入 public boolean add(E e) { //判断当前数据空间是否够插入数据 ensureCapacityInternal(size + 1); // Increments modCount!! //将数组后移一位,将新元素赋值给此位置 elementData[size++] = e; return true; } public void add(int index, E element) { rangeCheckForAdd(index); //判断当前数据空间是否够插入数据 ensureCapacityInternal(size + 1); // Increments modCount!! //用System.arraycopy,把index开始所有数据向后移动一位 System.arraycopy(elementData, index, elementData, index + 1, size - index); //将新元素插入到index位置 elementData[index] = element; size++; } private void ensureCapacityInternal(int minCapacity) { //如果数组为空 if (elementData == EMPTY_ELEMENTDATA) { //元素长度为默认长度和元素长度取较大值 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) //当元素个数大于数据长度的时候执行此方法 grow(minCapacity); } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //>>是移位运算符,相当于int newCapacity = oldCapacity + (oldCapacity/2),但性能会好一些, int newCapacity = oldCapacity + (oldCapacity >> 1);//根据老数组长度计算新数组长度,默认每次都是自增50%的大小 //下面判断保证新数组元素的个数在正常范围内 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity);//用新数组继续下面的处理 }
Vector可以看做是线程安全版的ArrayList,利用synchronized来实现线程安全,所以他的一大弊端就是性能不高,而且这个类太古老已经被弃用。
因此如果我们对线程安全性要求不高就可以使用ArrayList,如果必须保证线程安全可以使用Collections中提供的方法,转变成线程安全的类。
LinkedList底层是一个双向链表。LinkedList继承于AbstractSequentialList,和ArrayList一个套路。内部维护了3个成员变量,一个是当前链表的头节点,一个是尾部节点,还有是链表长度。
add(E e)操作
//从指定位置插入(中间插入) public void add(int index, E element) { checkPositionIndex(index); //如果插入位置和size相等相当于尾部插入 if (index == size) linkLast(element); else //否则就是中部插入 linkBefore(element, node(index)); } //尾部插入 public boolean add(E e) { //尾部插入的具体实现 linkLast(e); return true; } 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 原来尾节点的next指向新增的节点 l.next = newNode; //数量加一 size++; modCount++; } void linkBefore(E e, Node<E> succ) { // assert succ != null; //pred为插入节点的指向的前一个位置 final Node<E> pred = succ.prev; //新增的节点 final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; //如果指向前一个为空则说明这个链表为空,将新节点赋给头结点 if (pred == null) first = newNode; else //否则将原来index的pred的next指向新节点 pred.next = newNode; //数量加1 size++; modCount++; }
indexOf操作
从头开始遍历整个链表,如果没有就反-1,有就返回当前下标
public int indexOf(Object o) { int index = 0; if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) return index; index++; } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) return index; index++; } } return -1; }
remove操作
//无参的删除方法默认删除头结点 public E remove() { return removeFirst(); } //删除中间节点 public E remove(int index) { checkElementIndex(index); return unlink(node(index)); } //删除头结点 public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); } private E unlinkLast(Node<E> l) { // assert l == last && l != null; final E element = l.item; final Node<E> prev = l.prev; l.item = null; l.prev = null; // help GC last = prev; if (prev == null) first = null; else prev.next = null; size--; modCount++; return element; } //x的prev指向x的next E unlink(Node<E> x) { // assert x != null; final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; } private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; //删除头节点非常简单,就是把头节点的值清空,next清空 f.item = null; f.next = null; // help GC //把nextNode设为头节点 first = next; if (next == null) last = null; else //清空next的prev next.prev = null; //size减1 size--; modCount++; return element; }
List的特性:
对比LinkedList和ArrayList的实现方式不同,可以在不同的场景下使用不同的List 。ArrayList是由数组实现的,方便查找,返回数组下标对应的值即可,适用于多查找的场景 ,LinkedList由链表实现,插入和删除方便,适用于多次数据替换的场景。
ArrayList、LinkedList、Vector的区别和实现原理
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。