注意:我在Android Studio中分别查看了android SDK自带的jdk和我自己下载的jdk1.8,所看到的源代码并不相同。但是实现原理都是一样的,个人认为Android带的更好懂一些啊哈哈
更新:应该是OpenJDK/OracleJDK/ApacheHarmony 的差别,我真的有点糊涂,谁给我理一理他们的关系,而且Android到底要用什么了
List
ArrayList
用数组存储数据,初始大小0->10/12->*150%,当数据更多时,容量扩大50%,不同步,线程不安全。ArrayList适合随机查找和遍历,不适合插入和删除。
add函数里,扩容声明新数组的一段代码:
Object[] newArray = new Object[s +
(s < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : s >> 1)];
Vector
同ArrayList,初始大小为10,每次翻倍扩容。同步的,线程安全。因为同步的开销,效率低于ArrayList。
public synchronized void insertElementAt(E object, int location)
LinkedList
双向链表。可以被当作堆栈、队列或双端队列进行操作。非同步。
节点:
private static final class Link<ET> {
ET data;
Link<ET> previous, next;
Link(ET o, Link<ET> p, Link<ET> n) {
data = o;
previous = p;
next = n;
}
}
Set
HashSet
数据不允许出现重复,判断方式需要对象重写equals()和hashCode()函数。实现原理是内部封装一个HashMap,仅使用key,value用this(android sdk)或一个静态Object(jdk1.8)代替。
TreeSet
同,基于TreeMap。
Map
HashMap
利用数组+链表存储键值对。通过key.hash&(length-1)作为下标将键值对找到数组中合适的位置,并将其加在该位置链表的末尾。线程不安全,允许null值的存在,效率高于HashTable。新加的节点会放在链表首。当两个key的hash值相同时,首先找到数组中对应的链表首,然后开始循环,如果key指向的对象和键值对中key指向的对象是同一个,或者两者equals()和hashCode()都相同,则认为找到了,返还value。
Hashtable
和HashMap类似,但继承自Dictionary,线程安全,但不能接受key==null。 初始大小为11,如下图:
public Hashtable() {this(11, 0.75f);}
ConcurrentHashMap
利用锁分段技术实现一定程度上线程安全的同时,提高访问效率。将存储数据分段(Segment),访问Segment时,需要获得锁,可以同时访问不同分段的数据。不是绝对线程安全的,get时,不加锁,put时会加锁,remove时也会加锁。
以下引用自方腾飞——《聊聊并发(四)——深入分析ConcurrentHashMap》:
ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构, 一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素, 每个Segment守护者一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。
TreeMap
内部维护着一棵红黑树,保证了按大小顺序读出容器数据。
LinkedHashMap
LinkedHashMap 维护着一个运行于所有条目的双重链表。链表定义了迭代顺序(插入/访问)。不同步。允许使用 null 值和 null 键。
LinkedHashMap:public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
排序模式 :accessOrder。访问顺序: true;插入顺序: false。默认为插入顺序。
LinkedHashMap的一个用途时实现 LRU 缓存。LinkedHashMap 提供了removeEldestEntry(Map.Entry<K,V> eldest)
方法,默认返还false。可通过重写实现在需要删除的时候删除最旧的节点。
所有线程不安全的容器可以使用:Collections.synchronizedList/Map/Set(x)保证线程安全。
快速失败/安全失败(fast-fail/safe-fail)
- 安全失败基于对底层集合的拷贝,不受源集合上的修改的打扰。
- 当正在迭代的容器被另一个线程修改时,快速失败抛出异常:ConcurrentModicationException,安全失败不抛出该异常
- java.util包下都是快速失败,java.util.concurrent包下都是安全失败