★
List下面最常用的实现类ArrayList,底层使用数组存放内容,重要掌握如何初始化空间,如何扩容等等。
一,ArrayList
1.字段
private static final int DEFAULT_CAPACITY = 10; //默认初始化大小 10
private static final Object[] EMPTY_ELEMENTDATA = {}; //这两个字段是用来区分构造方式的,是默认构造还是固定了参数
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // non-private to simplify nested class access //存放元素的数组
private int size; //实际存储元数的个数
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //数组能达到的最大值。
注意底层存在的是Object对象,其次注意初始化大小是10. 注意最大值是integer.Max_value-8
2.构造方法
2.1 带大小的构造方法
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} ...
}
两个方式,如果初始化的大小>0,那么就直接new出来一个Object数组。
如果==0,那么直接使用默认的空元数。
2.2 无参构造:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
两个空集合在初始化在add()数据判断容量的时候初始化。
2.3 集合构造:
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;
}
}
将集合转成数组,如果容量不等于0,那么就使用工具类Arrrays复制过来。
如果等于0,那么就等同于参数为0的构造方法。
3.add()
添加元素,要确保容器的空间足够才能添加元素,如果空间不足,那么这里就涉及到了扩容机制。
为什么最大值等于Integer.Max_Value-8呢?
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
从注释中翻译过来,是要留一部分空间给header words,如果尝试获取更大内存,会造成oom错误。
为什么是Int,也可以这样理解,在操作方法中get,set,index里面的参数都是int,所以这里对于的也是Integer的最大值。其次-8,涉及到了数组在jvm中的分配方式,它需要一个数组头来表示数组的长度,64位的jvm中这个头的长度就是8。
3.1 add()
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
空间是否可行判断:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
传入参数,目前的元素+1,代表加入一个元素的大小空间,这里还有一个入口才到达内容,入口中调用了一个方法,calculate()
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
先判断容器是否是0空容器,如果是呢,就需要判断新增加的大小是否超过了10,没有就直接把空间扩容到10.否者返回实际空间大小。
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //被修改次数,这个字段在父类AbstractList中
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
这里也是一个入口,调用grow()方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //新的大小,x+x/2
if (newCapacity - minCapacity < 0) //与所需大小比较,如果装不下,就直接采用所需大小
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //溢出了,判断所需大小是否超过max,没超过就直接采用max
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity); //扩容
}
如果所需大小没有超过max值,那么直接采用max值。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
3.2 add(index,value)
添加元数到指定位置,这里涉及的就是元素的移动问题。
public void add(int index, E element) {
rangeCheckForAdd(index); //判断index是否合法
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,size - index);
elementData[index] = element;
size++;
}
4.get()
在素组中,可以随机获取元素,实现逻辑比较简单。
public E get(int index) {
rangeCheck(index);//判断 index是否合法
return elementData(index); //
}
范型的强制转换
E elementData(int index) {
return (E) elementData[index]; //直接数组随机获得
}
5.remove()
根据Index删除元素,返回该元素,首先就是要get()到,其次就是移动数组。
public E remove(int index) {
rangeCheck(index); //检测index是否合法
modCount++; //修改次数
E oldValue = elementData(index); //取得元素,返回
int numMoved = size - index - 1; //获得移动的数量
if (numMoved > 0) //移动后面的元素,向前移动
System.arraycopy(elementData, index+1, elementData, index,numMoved);
elementData[--size] = null; // 清除最后一个元素。并且size--
return oldValue;
}
还有一种删除指定元数的,这个就需要先找到元素,(可能还存在重复值,默认应该是从头到尾找到的第一个元素)
public boolean remove(Object o) {
if (o == null) { //传入的是null,则使用==来判断 否者使用equals来判断
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
对于其中的fastRemove()就和上面的move(index)内容差不多了。
6.获取指定元素的下标
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
二,LinkedList
1.底层实现
transient int size = 0;
transient Node<E> first; //指向链表首部的指针
transient Node<E> last; //指向链表尾部的指针
双向链表节点,同时这三个字段,也是LinkedList的所以字段。
节点的结构:内部类Node
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;
}
}
2.添加元数
普通的add(e)方法,调用的是添加到末尾。
public boolean add(E e) {
linkLast(e);
return true;
}
在头部插入:
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f); //疯转
first = newNode; //插入完毕
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
在构建的时候直接插入。
在尾部插入:
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;
size++;
modCount++;
}
在这里你就能发现Node类构造函数三个参数的巧妙使用。
在指定节点之前插入一个节点:
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev; //获得指定节点的前继节点
final Node<E> newNode = new Node<>(pred, e, succ);//插入新节点
succ.prev = newNode; //更新指定节点的前继节点
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
3.删除节点
默认的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;
}
删除头节点:
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item; //返回指定节点的值
final Node<E> next = f.next; //获得后继节点
f.item = null;
f.next = null; // help GC 方便回收
first = next; //删除头节点
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
同理存在删除尾节点,
删除指定节点
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;
}
4.获得节点
获得头节点:
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
三 vector
1.字段
protected Object[] elementData; //存放内容的容器
protected int elementCount; //数据个数
protected int capacityIncrement; //扩容增长系数
2.构造方法
public Vector() {
this(10);
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
注意默认的增长系数是0
3.添加元素
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
扩容机制:如果扩容系数<1,那么直接翻倍。>0,则直接添加扩容系数值。
private void ensureCapacityHelper(int minCapacity) {
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
4.获取元素
随机访问获取。
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
参考连接:
1.https://www.jianshu.com/p/6deae8f3e8d9