文章目录
List
ArrayList 和 Vector 底层使用的是数组进行存储,查询快增删慢,因为在增加时需要考虑数组的扩容问题,在扩容和删除时需要对数组进行重新拷贝新的数组。不同之处在于 ArrayList 是线程不安全的,Vector 线程是安全的。
LinkedList 底层采用的是双向列表结构,通过node节点中维护next节点和prev 节点来维护顺序
ArrayList
arrayList 是一个可变长度的动态数组,默认初始化的时候长度是0,
当进行add操作时,会判断数组是否为空,如果为空,默认初始化10个长度
当数组的长度已使用完毕后,再次执行add方法时,会进行数组的扩容,
新的扩容长度为 原来数组长度的1.5倍
public ArrayList() {
this.elementData = {};
}
// 扩容
private void grow(int minCapacity) {
// overflow-conscious code
// 判断旧的数组长度,新初始化的时候长度是0
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);
}
Vector
相比 ArrayList 来说,是线程安全的,是synchronized修饰的。结构和ArrayList 相同
vector 初始化的时候,默认初始化的数组长度是10,
当数组的商都使用完毕后,再次执行add方法时,会进行数组的扩容
新的数组为原来的1倍 或 扩容基础加上旧的数组长度
// 该构造方法,指定 初始化的数组长度,和扩容基础大小
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
// 扩容基础大小
this.capacityIncrement = capacityIncrement;
}
// 扩容
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 当使用无参构造Vector 或一个参数时, capacityIncrement 为0 ,
// 默认新的数组大小 = 旧的数组大小 + 旧的数组大小 即:扩容一倍
// 如果指定了扩容基础大小,则新的数组大小 = 旧的数组大小 + capacityIncrement
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);
}
LinkedList
linkedList 底层采用的是双向的链表结构,通过next 、prev node 节点组成双向链表结构
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;
}
}
//添加节点到最后一个节点上
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++;
}
Map
HashMap 、ConcurrentHashMap 无序集合,不同之处在于ConcurrentHashMap是线程安全的,并且不允许key和value 为空。
LinkedHashMap 和 TreeMap是有序集合,LinkedHashMap 有序是通过节点维护 before和 after 节点属性,而TreeMap 是通过实现了SortedMap
HashMap 可以key 、value 为空,线程不安全,HashTable 不能为空,线程安全
HashMap 参考 HashMap 源码分析学习
LinkedHashMap 参考 LinkedHashMap 源码分析学习
TreeMap
TreeMap 有序集合,实现了SortedMap,并且又可以通过构造传入排序的算法,默认按照key的排序规则进行排序,如果没有指定规则排序,则使用key 的类型所默认的类型排序规则进行排序,如果指定了自定义的排序规则,则使用自定义的规则
// 无自定义规则,使用默认的排序规则
public TreeMap() {
comparator = null;
}
// 使用自定义的排序规则
public TreeMap(SortedMap<K, ? extends V> m) {
comparator = m.comparator();
try {
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
}
// Entry 内部类,扩展了 left、right、parent 使之有序
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
}
ConcurrentHashMap
ConcurrentHashMap 的节点与HashMap 的节点大致是相同的,都是继承了Map.Entry,不同之处在于在ConcurrentHashMap.Node 节点的val 和 next 属性被 volatile 进行修饰。并大量使用了 Unsafe类 和 CAS 的安全替换.
transient volatile Node<K,V>[] table;
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
}
HashTable
Hashtable 不允许key、value为空,扩容时是原来的2倍+1,没有红黑树的概念
// 默认初始化大小是11,扩容因子是0.75f,同HashMap,不同是初始化hashMap是10
public Hashtable() {
this(11, 0.75f);
}
// Hashtable 不允许key和value值为null,如果为null,直接空指针
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
// key 为空直接空指针
int hash = key.hashCode();
// 计算该可以所在 数组中的位置
// 0x7FFFFFFF 2的31次方-1
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
// 循环查找index 位置下的entry中的next 节点下是否与该key值相同。如果key值已经存在,则替换旧的值,并返回旧的值
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
// 如果没有key没有存在,则进行添加
addEntry(hash, key, value, index);
return null;
}
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
// 如果当前存储的数据已经大于负载因子了,则进行rehash 扩容
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
// 通过 key 应该存在table中的位置 index ,获得该位置中的entry
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
// 直接将该key的值作为 获得该index 位置的entry 的next节点
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
// 新的容器大小是原来大小的2倍 + 1
int newCapacity = (oldCapacity << 1) + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
private static class Entry<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Entry<K,V> next;
}
Set
不可重复,HashSet 无序,LinkedHashMap、TreeSet 有序
HashSet 、LinkedHashSet 、TreeSet 实质上是维护了一个Map ,key是要存储的值,value 是一个 new Object();
HashSet
底部存储的是HashMap
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
//private static final Object PRESENT = new Object();
return map.put(e, PRESENT)==null;
}
LinkedHashSet
底部存储的是LinkedHashMap
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
TreeSet
底部存储的是TreeMap
public TreeSet() {
this(new TreeMap<E,Object>());
}
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
Queue
ArrayBlockingQueue 参考 ArrayBlockingQueue 源码分析学习