数据结构 |
存储特点 |
新增时间复杂度 |
删除时间复杂度 |
查找时间复杂度 |
JDK中使用的该结构的类 |
数组 |
采用一段连续的存储单元来存储数据 |
O(n) |
O(n) |
O(1)(给定下标查找)/ O(n)(给定值查找)/ O(logn)(使用二分查找) |
ArrayList |
线性链表 |
采用一组地址任意的存储单元存放数据元素 |
O(1) |
O(1) |
O(n) |
LinkedList |
二叉树 |
进行树状存储 |
O(logn) |
O(logn) |
O(logn) |
TreeSet、TreeMap |
哈希表(底层还是数组,使用哈希函数将关键字映射到数组的某个位置) |
采用关键码值映射到表中的一个位置来进行存储数据 |
O(1) |
O(1) |
O(1) |
HashMap |
//默认初始化容量大小为16
static final int DEFAULT_INITIAL_CAPACITY = 16;
//最大容量为2^30
static final int MAXIMUM_CAPACITY = 1073741824;
//默认装载因子是0.75,装载因子过大时,填入新的元素时,冲突的机会将很大,查找的成本高
//装载因子过小时,空间浪费
static final float DEFAULT_LOAD_FACTOR = 0.75F;
//链表节点转换红黑树节点的阀值,8个节点就转为红黑树
static final int TREEIFY_THRESHOLD = 8;
//红黑树节点转换链表节点的阀值,6个节点转为链表
static final int UNTREEIFY_THRESHOLD = 6;
//转红黑树时,table的最小长度为64
static final int MIN_TREEIFY_CAPACITY = 64;
//节点表(数组的每一个位置上存储一个节点表)
transient Node[] table;
//Entry集合
transient Set> entrySet;
//映射对的数量
transient int size;
//修改的次数(发生变化的次数)
transient int modCount;
//resize的临界值(=capacity*loadFactor)
int threshold;
//装载因子
final float loadFactor;
//构造方法参数
//initialCapacity: 初始化容量
//loadFactor: 加载因子
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0){
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
}else{
if (initialCapacity > MAXIMUM_CAPACITY){
initialCapacity = MAXIMUM_CAPACITY;
}
if (loadFactor <= 0 || Float.isNaN(loadFactor)){
throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
}
this.loadFactor = loadFactor;
//调用tableSizeFor方法, 该方法的作用是将输入的initialCapacity修改为相近的2的幂次方数, 因为HashMap的容量必须为2的幂次方,例:15----2^4
this.threshold = tableSizeFor(initialCapacity);
}
}
//采用默认的加载因子
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//采用默认的capacity、loadFactor
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
//创建一个内容为参数m的内容的哈希表
//采用默认的加载因子
public HashMap(Map extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
this.putMapEntries(m, false);
}
final void putMapEntries(Map extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
//数组还是空,初始化参数
if (table == null) {
float ft = ((float)s / loadFactor) + 1.0F;
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
if (t > threshold)
threshold = tableSizeFor(t);
}
//数组不为空,超过阈值就扩容
else if (s > threshold)
resize();
for (Map.Entry extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
//先经过 hash() 计算位置,然后复制指定 map 的内容
putVal(hash(key), key, value, false, evict);
}
}
}
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
//保存key-value的节点类
static class Node implements Map.Entry {
final int hash; //保存Hash值,也就是位置
final K key; //保存键
V value; //保存值
Node next; //指向下一个键值对
Node(int hash, K key, V value, Node next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey(){
return key;
}
public final V getValue(){
return value;
}
public final String toString(){
return key + "=" + value;
}
//返回key的hash值和key的hash值的异或结果
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry,?> e = (Map.Entry,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node[] tab; Node p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length; //当数组table为null时, 调用resize生成数组table, 并令tab指向数组table
if ((p = tab[i = (n - 1) & hash]) == null) //如果新存放的hash值没有冲突
tab[i] = newNode(hash, key, value, null); //则只需要生成新的Node节点并存放到table数组中即可
else { //否则就是产生了hash冲突
Node e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p; //如果hash值相等且key值相等, 则令e指向冲突的头节点
else if (p instanceof TreeNode) //如果头节点的key值与新插入的key值不等, 并且头结点是TreeNode类型,说明该hash值冲突是采用红黑树进行处理.
e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); //向红黑树中插入新的Node节点
else { //否则就是采用链表处理hash值冲突
for (int binCount = 0; ; ++binCount) { //遍历冲突链表, binCount记录hash值冲突链表中节点个数
if ((e = p.next) == null) { //当遍历到冲突链表的尾部时
p.next = newNode(hash, key, value, null); //生成新节点添加到链表末尾
if (binCount >= TREEIFY_THRESHOLD - 1) //如果binCount即冲突节点的个数大于等于 (TREEIFY_THRESHOLD(=8) - 1),便将冲突链表改为红黑树结构, 对冲突进行管理, 否则不需要改为红黑树结构
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) //如果在冲突链表中找到相同key值的节点, 则直接用新的value覆盖原来的value值即可
break;
p = e;
}
}
if (e != null) { // 说明原来已经存在相同key的键值对
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null) //onlyIfAbsent为true表示仅当不存在时进行插入, 为false表示强制覆盖;
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount; //修改次数自增
if (++size > threshold) //当键值对数量size达到临界值threhold后, 需要进行扩容操作.
resize();
afterNodeInsertion(evict);
return null;
}
//添加指定的键值对到 Map 中,如果已经存在,就替换
public V put(K key, V value) {
//调用 hash() 计算位置
//调用putVal()方法进行保存key-value
return putVal(hash(key), key, value, false, true);
}
final Node[] resize() {
//保存当前的节点集合数据
Node[] oldTab = table;
//保存旧的元素个数
int oldCap = (oldTab == null) ? 0 : oldTab.length;
//保存旧的扩容阀值
int oldThr = threshold;
//设置新的容量、扩容阀值
int newCap, newThr = 0;
if (oldCap > 0) {
//旧的容量大于最大容量
if (oldCap >= MAXIMUM_CAPACITY) {
//扩容阀值设为Integer的最大值
threshold = Integer.MAX_VALUE;
return oldTab;
}
//新的容量为旧的容量的两倍
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
//如果旧容量小于等于16,新的阈值就是旧阈值的两倍
newThr = oldThr << 1;
}
//如果旧容量为 0 ,并且旧阈值>0,说明之前创建了哈希表但没有添加元素,初始化容量=阈值
else if (oldThr > 0)
newCap = oldThr;
else {
//旧容量、旧阈值都是0,说明还没创建哈希表,容量为默认容量,阈值=容量*加载因子
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
//如果新的阈值为 0 ,就得用 新容量*加载因子 重新计算一遍
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
//更新阈值
threshold = newThr;
//创建新链表数组,容量是原来的两倍
@SuppressWarnings({"rawtypes","unchecked"})
Node[] newTab = (Node[])new Node[newCap];
//将新数组给变量table
table = newTab;
//将暂存旧数据的OldTab变量的数据遍历复制回table变量
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node e;
if ((e = oldTab[j]) != null) {
//旧的桶置为空
oldTab[j] = null;
//当前 桶 只有一个元素,直接赋值给对应位置
if (e.next == null)
//e.hash & (newCap - 1)=e.hash % newCap,重新索引
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
//如果旧哈希表中这个位置的桶是树形结构,就要把新哈希表里当前桶也变成树形结构
((TreeNode)e).split(this, newTab, j, oldCap);
else {
//保留旧哈希表桶中链表的顺序
Node loHead = null, loTail = null;
Node hiHead = null, hiTail = null;
Node next;
//循环赋值给新哈希表
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
//该方法的主要作用是将冲突链表改为红黑树
final void treeifyBin(Node[] tab, int hash) {
int n, index;
Node e;
//当数组的长度 hd = null, tl = null;
//遍历Node链
do {
//将Node对象转为Node对象
TreeNode p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null); //最后节点为空则停止
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
final Node getNode(int hash, Object key) {
Node[] tab;
Node first, e;
int n;
K k;
//e.hash & (newCap - 1)=e.hash % newCap ,得到目标的位置
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) { //first指向hash值对应数组位置中的Node节点
if (first.hash == hash && // 如果first节点对应的hash和key的hash相等(在数组相同位置,只是说明 hash&(n-1) 操作结果相等, 说明hash值的部分低位相等, 并不代表整个hash值相等), 并且first对应的key也相等的话, first节点就是要查找的
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) { //存在hash冲突
if (first instanceof TreeNode) //由红黑树对hash值冲突进行管理
return ((TreeNode)first).getTreeNode(hash, key); //查找红黑树
do { //hash值冲突是由链表进行管理
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null); //对链表进行遍历
}
}
return null;
}
public V get(Object key) {
Node e;
//调用getNode,通过hash值和key来获得对应的值,如果没有,返回null
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
//参数hash为key的hash值;
//参数key为要删除的key键;
//参数value为key对应的value;
//参数matchValue为true表明只有key在HashMap中对应值为value时才删除; 为false表示强制删除;
final Node removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node[] tab;
Node p;
int n, index;
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) { //在table中查找对应hash值
Node node = null, e; K k; V v;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;
else if ((e = p.next) != null) { //说明hash值存在冲突
if (p instanceof TreeNode) //hash值冲突由红黑树进行管理
node = ((TreeNode)p).getTreeNode(hash, key); //查找红黑树并返回该节点
else { //hash值冲突由链表管理
do {
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
if (node instanceof TreeNode)
((TreeNode)node).removeTreeNode(this, tab, movable); //从红黑树中删除该节点
else if (node == p)
tab[index] = node.next; //直接修改
else
p.next = node.next; //修改冲突链表
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
return null;
}
//删除key对应的键值对
public V remove(Object key) {
Node e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
static final int hash(Object key) {
int h;
//key不为null的情况下,返回key的哈希码异或上key哈希码进行无符号右移16位的结果
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}