基本特性
List
实现类 | 父类 | 数据结构 | 是否线程安全 | 扩容处理 | 是否接受null值 |
---|---|---|---|---|---|
LinkList | AbstractSequentialList | 双向链表 | 否 | 接受多个null值 | |
ArrayList | AbstractList | 数组 | 否 | 1.5倍扩容 | 接受多个null值 |
Vector | AbstractList | 数组 | 是 | 自定义或者2倍扩容 | 接受多个null值 |
Stack | Vector | 数组 | 是 | 2倍扩容 | 接受多个null值 |
Map
Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复
实现类 | 父类 | 数据结构 | 是否线程安全 | 扩容处理 | 是否接受null值 | 其他 |
---|---|---|---|---|---|---|
Hashtable | Dictionary | 哈希表(数组) | 是 | 2n+1 | key和value都不能为null值 | 遍历时获取的数据是随机的 |
WeakHashMap | AbstractMap | 哈希表 (数组) | 否 | 2倍扩容 | key和value可为null值 | 遍历时获取的数据是随机的 |
HashMap | AbstractMap | 哈希表(单链表+红黑树) | 否 | 2倍扩容 | key和value可为null值 | 遍历时获取的数据是随机的 |
LinkedHashMap | HashMap | 哈希表(双向链表) | 否 | 2倍扩容 | key和value可为null值 | 遍历时获取的数据和插入时是一样的 |
TreeMap | AbstractMap | 红黑树 | 否 | key不能为null,value可以为null | 遍历时获取的数据是递增排序好的 |
Set
用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复.
Set内部使用的是Map实现,Map的key不能重复,Set利用这一原理实现Set的元素值不重复.
实现类 | 父类 | 数据结构 | 是否线程安全 | 扩容处理 | 是否接受null值 | 其他 |
---|---|---|---|---|---|---|
TreeSet | AbstractSet(TreeMap实现) | 红黑树 | 否 | 2n+1 | 元素不能为null值 | 遍历时获取的数据是排序好的 |
HashSet | AbstractSet(HashMap实现) | 哈希表 (单链表+红黑树) | 否 | 2倍扩容 | 元素可为null值 | 遍历时获取的数据是随机的 |
LinkHashSet | HashSet(内部是LinkedHashMap实现) | 哈希表 (单链表+红黑树) | 否 | 2倍扩容 | 元素可为null值 | 遍历时获取的数据和插入时是一样的 |
1. List
1.1 LinkList
LinkList是一个双向链表结构的集合.
1.1.1 继承体系
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
1.1.2 私有变量
//元素数量
transient int size = 0;
//头节点
transient Node<E> first;
//尾节点
transient Node<E> last;
1.1.3 数据结构
双向链表
下面是链表的节点定义.
private static class Node<E> {
//元素值
E item;
//指向前驱节点
Node<E> next;
//指向后继节点
Node<E> prev;
}
1.1.4 是否线程安全
非线程安全
1.1.4 迭代器
ListIterator
1.1.5 是否接受null值
接受多个null值
1.2 ArrayList
1.2.1 继承体系
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
1.2.2私有变量
//初始容量
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//保存数据的数组
transient Object[] elementData;
//list的数据量
private int size;
//用于迭代时检测当前数组是否有更新
protected transient int modCount = 0;
1.2.3数据结构
ArrayList使用数组保存数据.
1.2.4扩容处理
当容量超过当前数据的长度时,ArrayList便会进行扩容.
获取新的容量,进行的是1.5倍扩容.
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//1.5倍扩容
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
扩容处理,也就是实现数组复制
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
1.2.5迭代器访问
注意迭代过程中其他线程不能修改ArrayList
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(234);
list.add(121);
//foreach
for(Integer dat:list){
System.out.println(dat);
}
System.out.println();
//Iterator
Iterator it = list.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
System.out.println();
//ListIterator
ListIterator listIterator = list.listIterator();
while(listIterator.hasNext())
{
listIterator.add(123);
System.out.println(listIterator.next());
}
1.2.6 是否线程安全
非线程安全
1.2.7 是否接受null值
可接受多个null值.
1.3 Vetor
1.3.1 继承体系
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
1.3.2 数据结构
使用数组保存数据
1.3.3 私有属性
//保存数据的数组
protected Object[] elementData;
//当前元素的数量
protected int elementCount;
//自定义每次扩容时增加的容量
protected int capacityIncrement;
//serialVersionUID
private static final long serialVersionUID = -2767605614048989439L;
1.3.4 扩容处理
每次添加元素时如果容量不够便会进行扩容处理.
获取新容量:
1.如果已经定义了每次扩容所增加的容量,那么扩容后的总容量就是当前的容量与自定义增加容量之和.
2.如果没定义,那就是两倍扩容.
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//获取新容量
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity <= 0) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
扩容处理,就是以新的容量复制旧的数组.
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
1.3.4 迭代器访问
public static void main(String[] args) {
//list
Vector<Integer> vector = new Vector<Integer>();
vector.add(234);
vector.add(121);
vector.add(null);
vector.add(null);
//foreach
for(Integer dat:vector){
System.out.println(dat);
}
System.out.println();
//Iterator
Iterator it = vector.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
System.out.println();
//ListIterator
ListIterator listIterator = vector.listIterator();
while(listIterator.hasNext())
{
listIterator.add(123);
System.out.println(listIterator.next());
}
}
1.3.5 是否线程安全
Vector使用synchronized同步了部分方法,因此是线程安全的.
1.3.6 是否接受null值
可以接受多个null值.
1.4 Stack
跳转目录
Stack是Vector的子类,是一个栈,因此特性和上述的Vector一致.
相关方法
//构造器
public Stack() { };
//向栈顶插入元素
public E push(E item){};
//弹出栈顶的元素(删除元素)
public synchronized E pop(){} ;
//获取栈顶的元素(不删除元素)
public synchronized E peek()
//检测栈是否为空
public boolean empty()
//获取元素的索引
public synchronized int search(Object o);
2 HashMap
2.1 Hashtable
2.1.1 继承体系
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable
2.1.2 私有变量
//保存各个节点的哈希表
private transient Entry<?,?>[] table;
//元素节点的数量
private transient int count;
//rehashed阀值,当元素数量大于threshold = (int)(capacity * loadFactor)进行rehashed
private int threshold;
//加载因子
private float loadFactor;
//记录当前对集合修改的次数,实现fail-fast机制
private transient int modCount = 0;
//序列化id
private static final long serialVersionUID = 1421746759512286392L;
2.1.3 保存结构
Entry是一个单链表结构
private transient Entry<?,?>[] table;
private static class Entry<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Entry<K,V> next;
}
2.1.4 实现原理
使用哈希表进行保存.
计算每个key的哈希值,通过哈希值计算保存位置索引值index.再存入table中,如果出现哈希冲突,则使用链地址法解决.多个元素节点Entry在同一个index处形成单链表.
2.1.5 index值计算
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
2.1.6rehash处理
可以看出,使用两倍扩容
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
//新容量是旧容量的两倍
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;
}
}
}
2.1.7是否线程安全
是.在方法上使用synchronized实现同步.
2.1.8是否接受null值
key和value都不能为null值
2.2 WeakHashMap
2.2.1 继承体系
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>
2.2.2 私有属性
//默认初始容量
private static final int DEFAULT_INITIAL_CAPACITY = 16;
//最大容量
private static final int MAXIMUM_CAPACITY = 1 << 30;
//负载因子
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
//存放数据的数组,类型为 Entry<K,V>[]
Entry<K,V>[] table;
//元素数量
private int size;
//扩容阀值,threshold = capacity * load factor
private int threshold;
//负载因子
private final float loadFactor;
//保存被gc回收的队列
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
//修改次数计数器,用于检测迭代过程中是否被修改
int modCount;
2.2.3 节点类型
可以看出节点是一个单链表的结构.
每次创建节点都会把节点的key进行弱引用.
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
}
2.3.4 数据结构
哈希表,使用链地址法解决哈希冲突.
2.3.5实现原理
WeakHashMap的key是“弱键”,即是WeakReference类型的。ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。实现步骤是:
1.新建WeakHashMap,将“键值对”添加到WeakHashMap中。实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表.
2.当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中.
3.当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对.
例子说明
public class WeakHashMapDemo {
public static void main(String[] args){
Object obj = new Object();
WeakHashMap weakHashMap = new WeakHashMap();
weakHashMap.put(obj,123);
//将obj去除强引用
obj = null;
//通知gc进行垃圾回收
System.gc();
Iterator it = weakHashMap.entrySet().iterator();
while(it.hasNext()){
Map.Entry en = (Map.Entry) it.next();
System.out.println("weakMap:" + en.getKey() + ": " + en.getValue());
}
}
}
1.当存在obj = null;时,weakHashMap中的obj对象没有了强引用,当进行gc回收后,weakHashMap中的obj对象即被回收,所以不会打印任何东西.
2.当注释掉obj = null;时,weakHashMap中的obj对象仍然存在强引用,当进行gc回收后,weakHashMap中的obj对象不会被回收,所以会打印以下内容.
weakMap:java.lang.Object@4c75cab9: 123
2.3.6 哈希值和索引值计算
//消除key为null时的影响
private static Object maskNull(Object key) {
return (key == null) ? NULL_KEY : key;
}
//计算哈希值
final int hash(Object k) {
int h = k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
//通过哈希值计算索引值
private static int indexFor(int h, int length) {
return h & (length-1);
}
2.3.7 同步quere和table表的数据
当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对.
private void expungeStaleEntries() {
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) x;
int i = indexFor(e.hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
2.3.8 扩容处理
进行的是两倍扩容
//put方法调用
resize(tab.length * 2);
2.3.9 是否接受null值
WeakHashMap对key为null的情况进行了处理,WeakHashMap可以接受key和value为null.但是只能存在一个key为null.
//消除key为null时的影响
private static Object maskNull(Object key) {
return (key == null) ? NULL_KEY : key;
}
2.3.10 是否是线程安全的
非线程安全.