一 概念
集合就是可以存储一组数据的一种数据结构
可以分为两大类
Collection:
List:用于存储一组数据,元素可以重复,有下标
Set:用于存储一组数据,元素不可以重复,无下标
Map: 用于存储键值对key->value,key不可以重复
二 实现类
List:
线程不安全:
ArrayList:底层通过数组实现,适合查询多,增删少
LinkedList:底层通过双向链表实现,适合增删多,查询少
线程安全:
CopyOnWriteArrayList:底层ReentrantLock + 数组实现
Vector:底层synchronized + 数组实现
Map:
线程不安全:
HashMap:
JDK1.7:底层通过数组 + 链表实现
JDK1.8:底层通过数组 + 链表 + 红黑树实现
线程安全:
HashTable:底层通过synchronized + 数组 + 链表实现
ConcurrentHashMap:
JDK1.7:底层通过Segment数组 + 数组 + 链表实现(Segment 继承ReentrantLock)
JDK1.8:底层通过synchronized + 数组 + 链表 + 红黑树实现
Set:
线程不安全:
HashSet:底层通过HashMap实现
线程安全:
CopyOnWriteArraySet:底层通过CopyOnWriteArrayList实现
三 源码解析
ArrayList:
核心属性:
// 默认容量10,但是new List时并未真正初始化数组
private static final int DEFAULT_CAPACITY = 10;
//用于初始化时的默认赋值
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 用于真正存放元素的数组, transient 是Java关键字,作用是序列化的时候忽略该字段
transient Object[] elementData; // non-private to simplify nested class access
// 真实存在元素的个数,List.size()返回值
private int size;
新增元素:
//初始化List
public ArrayList() {
//初始化并未真正的初始化数组,而是指向一个空的数组引用 DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//新增元素
public boolean add(E e) {
//计算数组的容量,第一次初始化数组,后面的根据条件判断是否扩容, size是当前元素个数,+1代表add元素时需要的容量
ensureCapacityInternal(size + 1); // Increments modCount!!
//把元素放入数组
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) {
//返回DEFAULT_CAPACITY (10), minCapacity中大的,初始化返回DEFAULT_CAPACITY
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
//用于防止并发修改
modCount++;
//所需最小容量与当前数组对比, 如果大于就需要扩容,初始化时elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA, 所以length == 0
if (minCapacity - elementData.length > 0)
//扩容
grow(minCapacity);
}
private void grow(int minCapacity) {
// 临时保存老的数组大小
int oldCapacity = elementData.length;
//新数组容量 = 老容量 + 老容量/2 相当于扩容1.5
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果需要容量大于计算出来的就用所需容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
//如果需要容量大于MAX_ARRAY_SIZE;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
//如果需要容量大于MAX_ARRAY_SIZE 返回Integer.MAX_VALUE
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
LinkedList
核心属性:
//当前实际存放的元素数量
transient int size = 0;
//第一个元素
transient Node<E> first;
//最后一个元素
transient Node<E> last;
新增元素:
//初始化
public LinkedList() {
}
//新增元素
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
//临时保存最后一个元素
final Node<E> l = last;、
//new 新元素, 第一个参数上一个元素, 第二个参数是当前元素, 第三个参数 下一个元素
final Node<E> newNode = new Node<>(l, e, null);
//把当前元素 赋值给last
last = newNode;
//如果last为null 相当于初始化,把当前元素也赋值给first
if (l == null)
first = newNode;
else
//把旧的最后一个元素的指针指向当前元素
l.next = newNode;
size++;
modCount++;
}
//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;
}
}
CopyOnWriteArrayList
写时复制,每次放入元素先加锁,复制数组创建新数组长度+1,替换旧数组
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8673264195747942595L;
//控制并发
transient final ReentrantLock lock = new ReentrantLock();
//存放元素的数组
private volatile transient Object[] array;
final Object[] getArray() {
return array;
}
final void setArray(Object[] a) {
array = a;
}
//构造器默认是0的数组
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
public boolean add(E e) {
final ReentrantLock lock = this.lock;
//防止并发
lock.lock();
try {
//获取当前数组
Object[] elements = getArray();
int len = elements.length;
//数组赋值,新长度为旧数组长度+1
Object[] newElements = Arrays.copyOf(elements, len + 1);
//放入元素
newElements[len] = e;
//替换存放元素的数组
setArray(newElements);
return true;
} finally {
//解锁
lock.unlock();
}
}
}
HashSet
核心属性:
//底层核心就是HashMap
private transient HashMap<E,Object> map;
//因为Map是键值对,所以Set中的元素就是Key, 值就是PRESENT
private static final Object PRESENT = new Object();
新增元素:
//初始化
public HashSet() {
map = new HashMap<>();
}
//新增元素
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//为什么value放入的是PRESENT不是NULL??
//remove方法返回boolean, 调用的是 map.remove(o)
//map的remove方法返回的是key对应的value, 如果没有返回null
//如果value放入的是null, 那么map.remove()不管这个key是否存在,返回的一定是null
//所以set中放入的value是PRESENT, 用于判断是否存在某个元素并成功移除
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
CopyOnWriteArraySet
public class CopyOnWriteArraySet<E> extends AbstractSet<E>
implements java.io.Serializable {
private static final long serialVersionUID = 5457747651344034263L;
//底层就是CopyOnWriteArrayList
private final CopyOnWriteArrayList<E> al;
//构造器
public CopyOnWriteArraySet() {
al = new CopyOnWriteArrayList<E>();
}
//新增元素
public boolean add(E e) {
return al.addIfAbsent(e);
}
}
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
public boolean addIfAbsent(E e) {
//加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//创建一个新数组长度是旧数组+1
Object[] newElements = new Object[len + 1];
//遍历旧数组看是否有相同
for (int i = 0; i < len; ++i) {
//已存在返回false
if (eq(e, elements[i]))
return false; // exit, throwing away copy
else
//不存在复制当前元素到新数组
newElements[i] = elements[i];
}
//把新加的元素放入数组
newElements[len] = e;
setArray(newElements);
return true;
} finally {
//解锁
lock.unlock();
}
}
}
HashMap
JDK1.8
ConcurrentHashMap
JDK1.7
JDK1.8