集合框架略读
ArrayList
就是一个数组
ArrayList底层以数组实现,是一种随机访问模式,再加上它实现了RandomAccess接口,因此查找也就是get的时候非常快
ArrayList在顺序添加一个元素的时候非常方便,只是往数组里面添加了一个元素而已
插入元素,删除元素, 会全部复制
因此,ArrayList比较适合顺序添加、随机访问的场景。
ArrayList transient Object[] elementData; 继承Serializable 但又不让elementData 序列化
因为有可能elementData没装满的话,会浪费 所以
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out array length s.writeInt(elementData.length); // Write out all elements in the proper order. for (int i=0; i<size; i++) s.writeObject(elementData[i]); if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
每次序列化的时候调用这个方法,先调用defaultWriteObject()方法序列化ArrayList中的非transient元素,elementData不去序列化它,然后遍历elementData,只序列化那些有的元素
ArrayList和Vector的区别
Vector 线程安全的 Collections.synchronizedList(list)
LinkList
就是一个双向链表 Node(item, first,next) 删除插入就是断开重连很快,
LinkList 和 ArrayList 对比 ArrayList add快 查找速度 LinkeList 寻找慢 添加快
CopyOnWriteArrayList 是concurrent下的类 线程安全的 任何可变的操作(add、set、remove等等)都是伴随复制这个动作的
set 时加了锁 并且操作的是volatile 可见不可改 CopyOnWriteArrayList适用于读操作远多于修改操作的并发场景中。
Hash'Map
内部存的是Node hash值相同值不同时,就串在后面
HashMap的table为什么是transient的 计算hashcode调用底层系统,所以每个机器计算不同
为了避免这一点,Java采取了重写自己序列化table的方法,在writeObject选择将key和value追加到序列化的文件最后面
private void writeObject(java.io.ObjectOutputStream s)throws IOException { int buckets = capacity(); // Write out the threshold, loadfactor, and any hidden stuff s.defaultWriteObject(); s.writeInt(buckets); s.writeInt(size); internalWriteEntries(s); } void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException { Node<K,V>[] tab; if (size > 0 && (tab = table) != null) { for (int i = 0; i < tab.length; ++i) { for (Node<K,V> e = tab[i]; e != null; e = e.next) { s.writeObject(e.key); s.writeObject(e.value); } } } }
ConcurrentHashMap HashMap和Hashtable的区别 Hashtable 枷锁了 底层存的Entry 不能空value
LinkedHashMap 继承自HashMap put 模板方法 实现了 afterNodeAccess 有序的
桶上分配照样 但是每个会按照Linklist 存入前后地址
每次访问一个元素(get或put),被访问的元素都被提到最后面去了
LRU (LRU即Least Recently Used,最近最少使用) LRUCache实现缓存LRU功能都是源自LinkedHashMap的。accessOrder afterNodeAccess
static final class Cache { private LinkedHashMap<String, CacheEntry> cache; private Type type; enum Type {Positive, Negative};
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
accessOrder 默认是false
(1)false,所有的Entry按照插入的顺序排列
(2)true,所有的Entry按照访问的顺序排列
第二点的意思就是,如果有1 2 3这3个Entry,那么访问了1,就把1移到尾部去,即2 3 1。每次访问都把访问的那个数据移到双向队列的尾部去,那么每次要淘汰数据的时候,双向队列最头的那个数据不就是最不常访问的那个数据了吗?换句话说,双向链表最头的那个数据就是要淘汰的数据。
public V get(Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return null; if (accessOrder) // true afterNodeAccess(e); return e.value; }
void afterNodeAccess(Node<K,V> e) { // move node to last LinkedHashMap.Entry<K,V> last; if (accessOrder && (last = tail) != e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.after = null; if (b == null) head = a; else b.after = a; if (a != null) a.before = b; else last = b; if (last == null) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } }