List集合
ArrayList(数组集合)
private static final int DEFAULT_CAPACITY = 10;//添加第一个元素时默认到10的大小
private static final Object[] EMPTY_ELEMENTDATA = {};//创建指定大小为0时返回该空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//不指定大小默认返回
transient Object[] elementData;//保存要添加的元素,当第一次添加时扩容到DEFAULT_CAPACITY
private int size;//实际lisk中集合的元素个数
构造函数
//当用户指定大小时调用
public ArrayList(int initialCapacity) {
//指定大小>0时创建大小相等的数组
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//指定大小==0时调用EMPTY_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
} else {
//<0抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//不指定大小时调用
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
注:底层由数组实现,存储的元素长度可变,查询效率高,增删效率低,线程不安全。使用频率很高
- 创建时: 通过无参构造方法的方式ArrayList()初始化,则赋值底层数Object[] elementData为一个默认空数组Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}所以数组容量为0,只有真正对数据进行添加add时,才分配默认DEFAULT_CAPACITY = 10的初始容量。
- 添加时:先检查当前容量是否需要扩容,如果需要扩容的话,扩容为原数组大小的1.5倍,新创建一个数组把原数据和新添加的数组copy到新数组中。
add(E e)
public boolean add(E e) {
//确认list容量,尝试容量加1,看看有无必要
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) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 如过添加后的list.size>低层数组的容量,扩容
if (minCapacity - elementData.length > 0)
//扩容大小,为原大小的1.5倍
grow(minCapacity);
}
//扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//原大小的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
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);
}
//扩容,返回一个新的数组
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType)
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
总结:
- 是否需要扩容。
- 需要,调用grow方法。
- 第一次扩容后,如果容量还是小于minCapacity,就将容量扩充为minCapacity。
- 足够:直接添加。
- 不足够:扩容。
add(int index, E element)
public void add(int index, E element) {
//检查下标是否越界
rangeCheckForAdd(index);
//是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//调用copy函数
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//添加
elementData[index] = element;
//list大小+1
size++;
}
get( int index)
//获取index
public E get(int index) {
//检查index是否在list的范围中
rangeCheck(index);
//fail-fast(快速失败机制)检查
checkForComodification();
//返回元素
return ArrayList.this.elementData(offset + index);
}
//判断是否越界
private void rangeCheck(int index) {
//越界抛出异常
if (index < 0 || index >= this.size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
总结:
- 检查角标
- 快速失败机制判断
- 返回值
set(int index, E element)
public E set(int index, E element) {
//检查是否越界,是否存在集合大小中
rangeCheck(index);
//获取index位置的元素
E oldValue = elementData(index);
//覆盖index位置的元素
elementData[index] = element;
//返回返回老的值
return oldValue;
}
总结:
- 检查时候越界
- 获取index元素赋值给oldValue
- 修改index的元素
- 返回oldValue
remove(int index)
public E remove(int index) {
//检查下标是否存在
rangeCheck(index);
//快速失败机制
modCount++;
//获取相应下标值
E oldValue = elementData(index);
//需要左移的个数
int numMoved = size - index - 1;
//调用copy
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//GC回收
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
总结:
- 检查角标
- 删除元素
- 计算出需要移动的个数,并移动
- 设置为null,让Gc回收
注:ArrayList(int initialCapacity)指定类集合中数组的大小,但没有集合的实际大小是由size属性来判断的,调用set方法会出现异常,ArrayList集合不适合做队列,但数组适合做循环队列
ArrayList集合常用方法
- boolean add(E e)
将指定的元素添加到此列表的尾部。
- void add(int index, E element)
将指定的元素插入此列表中的指定位置。
- boolean addAll(Collection c)
按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。
- boolean addAll(int index, Collection c)
从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。
- void clear()
移除此列表中的所有元素。
- Object clone()
返回此 ArrayList 实例的浅表副本。
- boolean contains(Object o)
如果此列表中包含指定的元素,则返回 true。
- void ensureCapacity(int minCapacity)
如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。
- E get(int index)
返回此列表中指定位置上的元素。
- int indexOf(Object o)
返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。
- boolean isEmpty()
如果此列表中没有元素,则返回 true
- int lastIndexOf(Object o)
返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。
- E remove(int index)
移除此列表中指定位置上的元素。
- boolean remove(Object o)
移除此列表中首次出现的指定元素(如果存在)。
- protected void removeRange(int fromIndex, int toIndex)
移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。
- E set(int index, E element)
用指定的元素替代此列表中指定位置上的元素。
- int size()
返回此列表中的元素数。
- Object[] toArray()
按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。
- T[] toArray(T[] a)
按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
- void trimToSize()
将此 ArrayList 实例的容量调整为列表的当前大小。
LinkedList
LinkedList底层是双向链表
基本结构
从结构上,我们还看到了LinkedList实现了Deque接口,因此,我们可以操作LinkedList像操作队列和栈一样~
//链表长度
transient int size = 0;
//头结点
transient Node<E> first;
//尾节点
transient Node<E> last;
//节点构造
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;
}
}
构造方法
//空的构造函数
public LinkedList() {
}
//自带集合的构造函数
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
add(E e)
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
l.next = newNode;
//大小+1
size++;
//快速失败
modCount++;
}
remove
public boolean remove(Object o) {
//如果不为空
if (o == null) {
//从头到尾的遍历链表
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
//断开连接
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
//断开连接
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;
}
get方法
//获取特定位置的
public E get(int index) {
//检查是否越界
checkElementIndex(index);
return node(index).item;
}
//遍历查找
Node<E> node(int index) {
//如果index在前半段
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {//index在后半段
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
set方法
set方法和get方法其实差不多,根据下标来判断是从头遍历还是从尾遍历
public E set(int index, E element) {
//检查越界
checkElementIndex(index);
//获取老的值
Node<E> x = node(index);
//赋值
E oldVal = x.item;
//将老的值修改
x.item = element;
return oldVal;
}