谈一谈–Java集合类
1、集合类有哪些
Java 中的集合类主要有四类: List列表、Queue队列 、Set集合 、Map映射, 它们的各自特点是:List和Queue 是有序的可重复的, Set 是不可重复 ,Map 是无序的 ,键值唯一。
2、集合类的关系
3、主要集合类的特性
3.1、 List列表 : 有序 可重复
1、ArrayList : 数组列表 ,内部是通过Array实现,对数据列表进行插入、删除操作时都需要对数组进行拷贝并重排序,因此在知道存储数据量时,尽量初始化初始容量,提升性能 。
2、LinkedList : 双向链表 每个元素都有指向前后元素的指针,顺序读取的效率较高,随机读取的效率较低
3、Vector : 向量 , 线程安全的列表,与ArrayList 一样也是通过数组实现的
4、Stack : 栈 , 后进先出 LIFO , 继承自Vector,也是用数组,线程安全的栈
3.2、 Queue队列:有序 可重复
1、ArrayDeque : 数组实现的的双端队列, 可以在队列两端插入和删除元素
2、LinkedList : 也属于双端队列
3、PriorityQueue : 优先队列 ,数组实现的二叉树, 完全二叉树实现的小顶堆(可改变比较方法)
3.3、 Set集合: 无序 不重复
1、HashSet 基于哈希实现的集合, 链表形式
2、LinkedHashSet
3、TreeSet 红黑树结构
3.4 、 Map映射/字典: 键值对 ,键唯一
1、HashMap 哈希映射 无序 , key 和 value 都可以为null
2、TreeMap 红黑树实现的, 可排序, 红黑树是一种自平衡二叉查找树
3、LinkedHashMap 链表映射 ,继承于HashMap,又实现了双向链表的特性 ,保留了元素插入顺序
4、IdentityHashMap 使用== 来检查键和值的相等性 , 成员使用的是严格相等
5、WeakHashMap 垃圾回收线程使用到
4、重点实现类介绍
4.1 、ArrayList
扩容机制
默认容量10 ,也可以指定容量初始化, 最大 Integer.MAX_VALUE - 8;
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) //溢出
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
private void grow(int minCapacity) {
// 获取到ArrayList中elementData数组的内存空间长度
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);
// 调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间
// 并将elementData的数据复制到新的内存空间
elementData = Arrays.copyOf(elementData, newCapacity);
}
快速失败机制
/**快速失败 ,并发访问时 产生的不一致问题 , modCount 和 expectedModCount 不一致时抛出异常 ConcurrentModificationException **/
//检查数组是否被修改
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
/**forEach 只在结尾检查一次modCount , 相对以前迭代,少检查,性能更高 **/
4.2 LinkedList
4.3、 HashMap
结构 :数组+链表,数组+(链表/红黑树)
扩容方式 : 初始容量 16 , 装填因子 0.75 , 桶中数量大于8 , 转红黑树(先判断容量64,否则先扩容), 小于6 转链表 , 扩容按倍增方式扩容
static final int hash(Object key) {
int h;
//计算key的hashCode, h = Objects.hashCode(key)
//h >>> 16表示对h无符号右移16位,高位补0,然后h与h >>> 16按位异或
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
/**
* 根据key的哈希值和key获取对应的节点
* getNode可分为以下几个步骤:
* 1.如果哈希表为空,或key对应的桶为空,返回null
* 2.如果桶中的第一个节点就和指定参数hash和key匹配上了,返回这个节点。
* 3.如果桶中的第一个节点没有匹配上,而且有后续节点
* 3.1如果当前的桶采用红黑树,则调用红黑树的get方法去获取节点
* 3.2如果当前的桶不采用红黑树,即桶中节点结构为链式结构,遍历链表,直到key匹配
* 4.找到节点返回null,否则返回null。
*
* @param hash 指定参数key的哈希值
* @param key 指定参数key
* @return 返回node,如果没有则返回null
*/
final Node<K, V> getNode(int hash, Object key) {
Node<K, V>[] tab;
Node<K, V> first, e;
int n;
K k;
//如果哈希表不为空,而且key对应的桶上不为空
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
//如果桶中的第一个节点就和指定参数hash和key匹配上了
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
//返回桶中的第一个节点
return first;
//如果桶中的第一个节点没有匹配上,而且有后续节点
if ((e = first.next) != null) {
//如果当前的桶采用红黑树,则调用红黑树的get方法去获取节点
if (first instanceof TreeNode)
return ((TreeNode<K, V>) first).getTreeNode(hash, key);
//如果当前的桶不采用红黑树,即桶中节点结构为链式结构
do {
//遍历链表,直到key匹配
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
//如果哈希表为空,或者没有找到节点,返回null
return null;
}
4.4 HashTable
初始容量 11 , 扩容 old*2+1 , 基于 synchronized 实现线程安全
4、遍历集合的工具
Iterator 和 Enumeration
5、操作集合的工具
Arrays 和 Collections