1.ArrayList存储结构
数组(数组是采用一段连续的存储单元来存储数据)
特点,查询快O(1)。删除插入慢(O(N))
数组查找遵循下面公式
数组 a[n] = 起始位置+(n*字节数);
2.ArrayList代码实现
2.1 首先看类的继承,实现关系。
继承了AbstractList,实现了List,RandomAccess, Cloneable,java.io.Serializable。
1. List ???(比较奇怪,AbstractList里面也实现了这个接口,这里又继承了,很奇怪。不知道为啥)
2. RandomAccess随机访问(get方法)
3. Cloneable 拷贝
4. Serializable序列化
2.2 基本属性
1. int DEFAULT_CAPACITY = 10; 数组初始化大小
2. Object[] EMPTY_ELEMENTDATA = {} ;空数组 用于有参构造创建数组。代码中能估算出list放多少数据,可以直接指定长度,优化速度。
3. Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};空数组 用于无参构造,就是一个空数组。
4. transient Object[] elementData; arrayList真正存放元素的地方,长度大于等于size
5. size 数组长度
2.3.构造方法
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
// 当 initialCapacity 为零时则是把 EMPTY_ELEMENTDATA 赋值给 elementData。
// 当 initialCapacity 大于零时初始化一个大小为 initialCapacity 的 object 数组并赋值给 elementData。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
//无参构造器,构造一个容量大小为 10 的空的 list 集合,但构造函数只是给 elementData 赋值了一个空的数组,其实是在第一次添加元素时容量扩大至 10 的。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
//将 Collection 转化为数组,数组长度赋值给 size。
//如果 size 不为零,则判断 elementData 的 class 类型是否为 ArrayList,不是的话则做一次转换。
//如果 size 为零,则把 EMPTY_ELEMENTDATA 赋值给 elementData,相当于new ArrayList(0)。
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
2.4 常用的方法
- 1.add
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#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);
//依次复制插入位置及后面的数组元素,到后面一格。
//因此复制完后,添加的下标位置和下一个位置指向对同一个对象
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//再将元素赋值给该下标
elementData[index] = element;
size++;
}
3.1 ensureCapacityInternal里面有个扩容的操作
private void grow(int minCapacity) {
//获取当前数组长度
int oldCapacity = elementData.length;
//默认将扩容至原来容量的 1.5 倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果1.5倍太小的话,则将我们所需的容量大小赋值给newCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果1.5倍太大或者我们需要的容量太大,
//那就直接拿 newCapacity = hugeCapacity(minCapacity)
//(minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE : MAX_ARRAY_SIZE 来扩容
// MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//然后将原数组中的数据复制到大小为 newCapacity 的新数组中,
//并将新数组赋值给 elementData。
elementData = Arrays.copyOf(elementData, newCapacity);
}
- 2.迭代
private class Itr implements Iterator<E> {
// 代表下一个要访问的元素下标
int cursor;
// 代表上一个要访问的元素下标
int lastRet = -1;
//代表对 ArrayList 修改次数的期望值,初始值为 modCount
//如果下一个元素的下标等于集合的大小 ,就证明到最后了
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
//对 cursor 进行判断,看是否超过集合大小和数组长度
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
//自增 1。开始时,cursor = 0,lastRet = -1;
//每调用一次next方法,cursor和lastRet都会自增1。
cursor = i + 1;
将cursor赋值给lastRet,并返回下标为 lastRet 的元素
return (E) elementData[lastRet = i];
}
public void remove() {
//判断 lastRet 的值是否小于 0
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//直接调用 ArrayList 的 remove 方法删除下标为 lastRet 的元素
ArrayList.this.remove(lastRet);
cursor = lastRet;
//将 lastRet 重新赋值为 -1,
lastRet = -1;
//将 modCount 重新赋值给 expectedModCount。
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
//判断expectedModCount和modCount是否相等
//ConcurrentModificationException
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
1.LinkedList存储结构
双向链表
特点,插入删除快。查询慢
2.LinkedList代码实现
2.1 首先看类的继承,实现关系。
1. 继承了AbstractSequentialList,实现了List,Deque<?>,Cloneable, java.io.Serializable。
2. Deque 队列
3. Cloneable 拷贝
4. Serializable序列化
2.2 基本属性
Node(看next,prev很明显的双向链表)
2.3 构造方法
2.4 常用方法
- add 一个参数,默认尾插
- get方法
get这边算是二分查找吧