[7]-集合类

HashMap(容量,负载因子):非线程安全

  • 数组+链表实现
  • 当Entry节点数量超过容量负载因子,进行扩容*2,进行resize()

  • 对key的hashCode()进行indexfor获得位置,因为取余效率低

  • 如果该位置没有碰撞、直接存到bucket中;如果发生碰撞,遍历链表有没有相等的key,
    • 有就替换该Entry的value;没有就addEntry(),是否扩容,创建一个Entry作为链表头
  • jdk8后来做的改进是、如果碰撞导致链表过长,长度超过8,把链表转换成红黑树

  • resize()就是线程不安全的源头:多线程同时resize()可能会产生Entry的环

Entry put(K key,v value){
    if (key == null) { return putForNullKey(value); } 
    int i = indexFor(hash(key), table.length);  
    for (Entry< K,V> e = table[i]; e != null; e = e.next) {  
        if (e.hash == hash && (eKey== key || key.equals(eKey))) {  
            V oldValue = e.value;  
            e.value = value;  
            e.recordAccess(this);  
            return oldValue;  
        }  
    } 
    modCount++;  
    addEntry(hash, key, value, i);  
    return null;   
}

void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }
    createEntry(hash, key, value, bucketIndex);
}

void createEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}

void transfer(Entry[] newTable){
    Entry[] src = table;
    int newCapacity = newTable.length;
    for (int j = 0; j < src.length; j++) {
        Entry< K,V> e = src[j];
        if (e != null) {
            src[j] = null;
            do {
                Entry<K,V> next = e.next;
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            } 
            while (e != null);
        }
    }
}

Hashtable:所有函数都是同步有锁

  • 效率低下、所有访问HashTable的线程都必须竞争同一把锁
  • 继承`Dictionary类,实现了Map接口
  • 他的key、value都不能是null

ConcurrentHashMap:锁分段、元素HashMap

  • 由Segment[]数组构成、每一个Segment与HashMap类型相似
  • Entry的变量final类型(除了value:volatile)
  • 除了容量、负载因子,还加了一个参数 concurrencyLevel
  • egment[]数组的长度是大于或等于concurrencyLevel的最小的2的N次方值

  • 同步有锁、锁分段技术、

    • 首先将数据分成一段一段的存储,然后给每一段数据配一把锁,
    • 当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问
  • 定位Segment:HashMap中的hash()一样,为了减少哈希冲突

  • Segment< K,V> segmentFor(hash)右移28位、让高四位参与到运算中)&(segmentMask掩码默认是8)
  • get()是无锁的:判断entry的value是否为null,是就加锁

public V get(Object key) {
    int hash = hash(key);
    return segmentFor(hash).get(key, hash);
 }

 V get(Object key, int hash) {
    //volatile修饰count
    if (count != 0){ 
        HashEntry<K,V> e = getFirst(hash); 
        while (e != null) {
            if (e.hash == hash && key.equals(e.key)) {
                V v = e.value;
                if (v != null) {
                    return v;
                }
                return readValueUnderLock(e);
            }
            e = e.next;
        }
    }
    return null;
}

  • put()、remove()、ReentrantLock加锁
  • 修改:突然另一个进程修改了我们想get()的Entry的value:value是volatile的,没关系
  • 增加:突然另一个线程在链表头put()了一个我们想get()的Entry
    • if (v != null) 的判断,对new出来的对象进行状态检测,不为null,返回
    • 还为null的话,readValueUnderLock(),锁定之后返回一个值(value不允许为null)
  • 删除:突然另一个进程删除了我们想get()的Entry
    • 因为next变量是final的,所以只能在table[index]重链接一条单链表,e1-e2-e3-e4,删e3
    • 变成e2-e1-e4,如果我们已顺着原链表到了e1了,继续顺着原来的走,仍会返回e3
  • ConcurrentHashMap的迭代器不是快速失败

List

  • ArrayList:动态扩容1.5倍(下标访问-尾部插入高效-其他位置System.arraycopy())
  • LinkedList:双向链表(两头插入删除高效-其他位置)
  • CopyOnWriteArrayList(java.util.concurrent):只有写锁、没有读锁。适合读多写少的场景
    • 在修改时先复制一个快照Arrays.copyof(),每次写都在新数组写,指针指向新数组

Map

  • LinkedHashMap:双向链表:按Entry插入顺序排序:LRU(accessOrder=true)
  • TreeMap:红黑树实现、iterator()有序遍历key、Comparable或Comparator

Queue:两端出入的List

  • PriorityQueue:二叉堆:iterator()不保证有序遍历
  • LinkedList:双向链表:唯一允许放入null的Queue
  • ArrayDeque:循环数组:push尾增,pop头增,尾追上头无空间

  • ConcurrentLinkedQueue:线程安全:单向链表:CAS无锁
  • PriorityBlockingQueue:线程安全:二叉堆:读写一把锁:阻塞接口,无阻塞特性

  • ArrayBlockingQueue:线程安全阻塞:循环数组:读写一把锁
  • LinkedBlockingQueue:线程安全阻塞:链表:两把锁

public class LruLinkedHashMap<K,V> extends LinkedHashMap<K,V>{
    public int cap;
    LruLinkedHashMap(int cap){
        //LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder)
        super(cap,0.75f,true);
        this.cap=cap;
    }
    public boolean removeEldestEntry(Map.Entry<K, V> eldest){
        return size()>cap;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章