Conllection
Conllection是按照单个元素存储的
List
List是线性数据结构的主要实现:
- 集合元素存在明确的pre和next
- 集合存在明确的head和tail
- 元素遍历结果稳定
ArrayList
- 容量可变的非线程安全集合
- 使用数组进行存储,扩容时创建更大的数组空间,把原有数据复制到新数组
- 支持元素的快速访问,插入和删除速度较慢
ArrayList数据结构
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 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 == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
// 有参构造方法
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);
}
}
// 无参构造方法,为{},在第一次添加元素的时候扩容至默认容量
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
}
ArrayList底层实现是Object[],默认容量为10;
添加元素add()
public boolean add(E e) {
// 判断size + 1 <= 当前数组长度;否则就执行扩容grow()方法
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
扩容grow()方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
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); // 数组拷贝
}
所以,整体的流程就是:
在add元素的过程中,如果数组未初始化,则初始化为默认容量大小的数组(执行扩容为10),然后插入元素返回;如果数组已经初始化,判断数组长度是否满足所需容量,满足则插入元素返回;不满足则执行扩容为原长度的1.5倍。
LinkedList
- 双向链表
- 每个元素都封装为Node节点
- 插入删除快,随机访问慢
- 同时实现了Deque接口,即double-ended queue双端队列,但是通常不用它作为双端队列。通常我们在刷题的时候如果用到队列/栈的话,推荐使用:
Deque<Integer> deque = new ArrayDeque<>(); // 双端队列既可以作为队列也可以作为栈来使用
数据结构
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
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;
}
}
}
有头尾节点,Node节点存数据和前驱后继
添加元素
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++;
}
Queue
Set
Map
Map是按照Key-Value存储的