集合框架体系
ArrayList(jdk1.7)
基本结构
初始化
三个构造方法。传入初始容量会直接设置数组大小,不传就设为空数组,或者传入Collection,转换并确保一下类型
扩容机制
- 每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度。如果长度不足,将会进行扩容。(add方法中会调用ensureCapacityInternal -> ensureExplicitCapacity -> grow)
- grow方法是扩容操作的核心代码,可以看到,这个方法是先计算一个新的数组(新数组长度=原长+(原长>>1)),然后调用Arrays.copyOf方法将旧数组复制到新的数组中。
- 在添加大量元素前,可以使用ensureCapacity来手动增加ArrayList的容量,以减少递增式再分配的数量。
add
remove
删除操作是add()操作的逆过程,需要将删除点之后的元素向前移动一个位置。
需要注意的是为了让GC起作用,必须显式的为最后一个位置赋null
值。
System.arraycopy
Vector(jdk1.7)
grow
与ArrayList不同的是,Vecotor根据步长来扩容
如果capacityIncrement大于0,则扩容为(+capacityIncrement
),否则扩容两倍(+oldCapacity
)
LinkedList(jdk1.7)
LinkedList同时实现了List接口和Deque接口,也就是说它既可以看作一个顺序容器,又可以看作一个队列(Queue),同时又可以看作一个栈(Stack)。 (不过,关于栈或队列,现在的首选是ArrayDeque,它有着比LinkedList(当作栈或队列使用时)有着更好的性能。)
基本结构
add
addLast(E e)
add(int index, E element)
remove
clear
为了让GC更快可以回收放置的元素,需要将node之间的引用关系赋空。
HashMap(jdk1.7)
基本结构
初始化
构造函数
第一次put时
get
put
put方法首先会对map做一次查找,看是否包含,如果已经包含则直接返回
否则会通过addEntry
方法插入新的entry,插入方式为头插法。
transfer造成的链表成环
- 当两个线程同时插入元素,并且同时发现需要扩容时,由于扩容时会改变链表元素的顺序,在线程1未完成扩容的时候,线程2抢占到了资源完成了扩容
- 这时线程1继续执行,由于线程1中有一个临时变量e,它的next会被指向新数组的一个元素,此时继续执行会造成错误的赋值,导致链表成环。
Hashtable(jdk1.7)
和HashMap的不同
- 构造函数中直接初始化table
- Hashtable默认的容量是11。
- 下标使用取模操作
int index = (hash & 0x7FFFFFFF) % tab.length;
- 每次扩容,容量变为原来的2n+1,
(oldCapacity << 1) + 1
- key和value都不能为空
HashMap(jdk1.8)
什么情况下从链表转为红黑树?
当Map链表长度大于或等于阈值(默认为 8)并且还满足容量大于或等于 MIN_TREEIFY_CAPACITY(默认为 64)的要求,就会把链表转换为红黑树。 当红黑树的节点小于或等于 6 个以后,会恢复为链表。
- 为什么是8? 服从0.5的泊松分布 每个bucket有0个元素的概率是0.6,1是0.3,2是0.07,8是亿分之六 因此链表长度超过 8 是几乎不可能的
ConcurrentHashMap(jdk1.7)
jdk1.7使用 分段锁机制 实现 jdk1.8使用 数组+链表+红黑树+CAS原子操作 实现
基本结构
快速失败fail-fast
快速失败机制,是java集合中的一种错误检测机制。当在迭代集合的过程中该集合在结构上发生改变的时候,就有可能会发生fail-fast,即抛出 ConcurrentModificationException异常。
评论