Created by Li Yuntao / @dust_jead
07/15/2015
本次presentation粗略介绍jdk中常用集合类,关注其特征与实现方式
首次使用,创建大小为10的内部数组
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
超出当前数组容量时会扩容两倍,使用System.arrayCopy()复制到新的数组
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
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);
}
// java api
List lst1 = new ArrayList(xx.size());
// guava api 传入固定大小
List lst2 = Lists.newArrayListWithCapacity(initialArraySize);
// guava api 传入预估大小
List lst3 = Lists.newArrayListWithExpectedSize(estimatedSize);
Queue queue = new LinkedList();
多读少写场景:白/黑名单,商品类目的访问和更新
JDK 1.8改动:增加阈值==8,当哈希桶挂在的链表长度超过阈值时,将链表转化为红黑树
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do {
TreeNode<K,V> 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);
}
}
扩展HashMap的Node节点, 实现为一个双向链表
iterator()时按Entry的插入顺序来排序
/**
* HashMap.Node subclass for normal LinkedHashMap entries.
*/
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
实现:红黑树
继承红黑树结构的特性,插入删除最坏耗时O(lgn)
// 取最大最小Key/Entry
public K firstKey() { return key(getFirstEntry()); }
public K lastKey() { return key(getLastEntry()); }
Map.Entry<K,V> firstEntry();
Map.Entry<K,V> lastEntry();
// 取上一个下一个Key/Entry
K lowerKey(K key);
K higherKey(K key);
Map.Entry<K,V> lowerEntry(K key);
Map.Entry<K,V> higherEntry(K key);
// 减取TreeMap的片段
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive);
NavigableMap<K,V> headMap(K toKey, boolean inclusive);
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);
分段思想/锁分离
默认16把写锁,当多个线程的访问落在不同的段时,线程间不会存在锁竞争
// 默认16把写锁
Segment<K,V>[] segments = (Segment<K,V>[])
new Segment<?,?>[DEFAULT_CONCURRENCY_LEVEL];
/**
* Stripped-down version of helper class used in previous version,
* declared for the sake of serialization compatibility
*/
static class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
final float loadFactor;
Segment(float lf) { this.loadFactor = lf; }
}
SkipList结构
SkipList搜索
几乎所有的Set内部都由对应的Map实现,Value是一个假值
线程安全队列(阻塞): ArrayBlockingQueue/LinkedBlockingQueue
LinkedBlockingDeque/PriorityBlockingQueue
This class is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue.
方法\处理方式 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
---|---|---|---|---|
插入方法 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除方法 | remove() | poll() | take() | poll(time,unit) |
检查方法 | element() | peek() | 不可用 | 不可用 |
- 关于Java集合的小抄
- Java中的CopyOnWrite容器
- Java Performance Tuning Guide
CopyOnWriteArrayList的add()
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 复制出新数组
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 把新元素添加到新数组里
newElements[len] = e;
// 把原数组引用指向新数组
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
整个操作是在持有锁的情况下执行的,否则多个线程可能会copy出多份副本出来。