# 容器概述

# List,Set,Map三者区别:

  • List: 线性,有序,可重复
  • Set: 线性,无序,不可重复
  • Map: 键值对存储。 key时无序,不可重复;value是无序,可重复;

# Collection集合定义及其子类

  • 抽象结构定义
public interface Collection<E> extends Iterable<E> {
    int size();

    boolean isEmpty();

    boolean contains(Object o);

    Iterator<E> iterator();

    Object[] toArray();

    boolean add(E e);

    boolean remove(Object o);

    void clear();

    //...省略部分抽象方法
}
  • List:
    • ArrayList数组
    • LinkedList链表
    • Vector数组
public interface List<E> extends Collection<E> {
    //... 省略部分父类抽象定义

    default void sort(Comparator<? super E> c) {
            Object[] a = this.toArray();
            Arrays.sort(a, (Comparator) c);
            ListIterator<E> i = this.listIterator();
            for (Object e : a) {
                i.next();
                i.set((E) e);
            }
        }

    E get(int index);

    E set(int index, E element);

    void add(int index, E element);

    E remove(int index);

    int indexOf(Object o);

    int lastIndexOf(Object o);

    List<E> subList(int fromIndex, int toIndex);

}
  • Set:
    • HashSet: 无序,唯一;基于HashMap实现,底层采用HashMap保存元素;
    • LinkedHashSet: 基于LinkedHashMap实现;
    • TreeSet: 有序,唯一;红黑树实现;
public interface Set<E> extends Collection<E> {
    //... 完美继承Collection抽象定义
    //注意,它没有 get(index) 方法
}

# Map字典定义及其子类

  • HashMap: java8以前是: 数组+链表实现;java8以后是: 数组+链表+红黑树;
  • LinkedHashMap: 增加了双向链表,使Map结构可以保持键值对的插入顺序;
  • Hashtable(t小写): 数组+链表;
  • TreeMap: 红黑树
public interface Map<K,V> {
    int size();
    
    boolean isEmpty();

    boolean containsKey(Object key);

    boolean containsValue(Object value);

    V get(Object key);

    V put(K key, V value);

    V remove(Object key);

    void clear();

    Set<K> keySet();

    Collection<V> values();

    Set<Map.Entry<K, V>> entrySet();

     default V getOrDefault(Object key, V defaultValue) {
            V v;
            return (((v = get(key)) != null) || containsKey(key))
                ? v
                : defaultValue;
        }

     default V putIfAbsent(K key, V value) {
             V v = get(key);
             if (v == null) {
                 v = put(key, value);
             }
     
             return v;
         }

     default V computeIfAbsent(K key,
                 Function<? super K, ? extends V> mappingFunction) {
             Objects.requireNonNull(mappingFunction);
             V v;
             if ((v = get(key)) == null) {
                 V newValue;
                 if ((newValue = mappingFunction.apply(key)) != null) {
                     put(key, newValue);
                     return newValue;
                 }
             }
     
             return v;
         }

    interface Entry<K,V> {
        K getKey();        

        V getValue();

        V setValue(V value);
    }
    //... 省略部分抽象定义
}

# List列表

# ArrayList与Vector的区别

  • ArrayList: 线程不安全
  • Vector: 线程安全

# ArrayList 与 LinkedList 的区别

  • 底层数据结构: -> ArrayList插入删除慢,查找快,LinkedList相反.
    • ArrayList: 采用对象数组存储
    • LinkedList: 采用双向链表,JDK7取消了循环
  • ArrayList查找(随机访问)速度快于LinkedList原因:
// ArrayList的查找方法
class ArrayList{
    public E get(int index) {
            rangeCheck(index);
    
            return elementData(index);
        }
    //按位置查找
    E elementData(int index) {
            return (E) elementData[index];
        }

    //按值查找
     public int indexOf(Object o) {
            if (o == null) {
                for (int i = 0; i < size; i++)
                    if (elementData[i]==null)
                        return i;
            } else {
                for (int i = 0; i < size; i++)
                    if (o.equals(elementData[i]))
                        return i;
            }
            return -1;
        }
}

//LinkedList的查找方法
class LinkedList{
    public E get(int index) {
            checkElementIndex(index);
            return node(index).item;
        }

    //按位置查找
    Node<E> node(int index) {
            //从头指针开始移动
            if (index < (size >> 1)) {
                Node<E> x = first;
                for (int i = 0; i < index; i++)
                    x = x.next;
                return x;
            } else {
                //从尾指针开始移动
                Node<E> x = last;
                for (int i = size - 1; i > index; i--)
                    x = x.prev;
                return x;
            }
        }

    //按值查找
    Node<E> node(int index) {
            // assert isElementIndex(index);
    
            if (index < (size >> 1)) {
                Node<E> x = first;
                for (int i = 0; i < index; i++)
                    x = x.next;
                return x;
            } else {
                Node<E> x = last;
                for (int i = size - 1; i > index; i--)
                    x = x.prev;
                return x;
            }
        }
}
  • ArrayList插入删除慢于LinkedList原因(个人感觉不一定):
//ArrayList的插入方法
class ArrayList{  
    //直接插入
    public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }

    //按位置插入
    public void add(int index, E element) {
            rangeCheckForAdd(index);
    
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            elementData[index] = element;
            size++;
        }
}

//LinkedList的插入方法
class LinkedList{
    //按值插入
    public boolean add(E e) {
            linkLast(e);
            return true;
        }
    void linkLast(E e) {
            final Node<E> l = last;
            final Node<E> newNode = new Node<>(l, e, null);
            last = newNode;
            if (l == null)
                first = newNode;
            else
                l.next = newNode;
            size++;
            modCount++;
        }

    //按位置添加
    public void add(int index, E element) {
            checkPositionIndex(index);
    
            if (index == size)
                linkLast(element);
            else
                linkBefore(element, node(index));
        }
    void linkBefore(E e, Node<E> succ) {
            // assert succ != null;
            final Node<E> pred = succ.prev;
            final Node<E> newNode = new Node<>(pred, e, succ);
            succ.prev = newNode;
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            size++;
            modCount++;
        }
}
  • 内存占用的区别
    • ArrayList: 采用对象数组,数量不够时,采用扩容机制进行扩容;
    • LinkedList: 采用双向链表,采取新增节点的方式进行存储;节点除了包含数据本身外,还包括前序节点,后续节点;

# ArrayList的扩容机制

  • ArrayList的构造函数

    • 空参构造: jdk7直接构造了大小为10的对象数组; jdk8构造了空数组,在第一次插入数据时扩容为大小为10的对象数组;
    • 初始容量参数的构造: 用户自己指定初始化数组的大小;
    • 构造包含指定元素的构造: 初始化数组的大小,就是指定元素的大小;
  • 动态扩容

    • 扩容大小: 扩容至1.5倍左右; 原大小是偶数时,就是扩容1.5倍; 原大小是奇数时,就是扩容1.5倍左右;
//ArrayList动态扩容方法
class ArrayList{
    private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            // 新大小 = 原大小*1.5
            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);
        }
}

# Set集合

# Comparable 与 Comparator的区别

  • Comparable: 出自java.lang包, 使用 compareTo(obj) 进行排序
  • Comparator: 出自java.util包, 使用 compare(obj1,obj2) 进行排序

# HashSet,LinkedHashSet,TreeSet异同

  • HashSet: 底层采用 HashMap,线程不安全,可存储null值;
  • LinkedHashSet: HashSet的子类,能够按照添加的顺序遍历
  • TreeSet: 底层使用红黑树

# HashSet

  • 抽象结构:
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    private transient HashMap<E,Object> map;

    private static final Object PRESENT = new Object();
    
    public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }

}

# LinkedHashSet

  • 抽象结构
public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {

    @Override
    public Spliterator<E> spliterator() {
            return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
    }
    
    //... 省略部分抽象定义
    
    }

# TreeSet

  • 抽象结构
public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    //hashMap
    private transient NavigableMap<E,Object> m;
    
    public boolean add(E e) {
            return m.put(e, PRESENT)==null;
        }

    private static final Object PRESENT = new Object();
}

# Map字典

# HashMap

  • 抽象结构:
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    final float loadFactor;

    transient Node<K,V>[] table;

    transient int size;

    public HashMap(int initialCapacity, float loadFactor) {
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal initial capacity: " +
                                                   initialCapacity);
            if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new IllegalArgumentException("Illegal load factor: " +
                                                   loadFactor);
            this.loadFactor = loadFactor;
            this.threshold = tableSizeFor(initialCapacity);
        }

    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
        
        Node(int hash, K key, V value, Node<K,V> next) {
                    this.hash = hash;
                    this.key = key;
                    this.value = value;
                    this.next = next;
                }

        public final V setValue(V newValue) {
                    V oldValue = value;
                    value = newValue;
                    return oldValue;
                }
    }

    //结果为 比 cap大的,最小二次幂数
    static final int tableSizeFor(int cap) {
            int n = cap - 1;
            n |= n >>> 1;
            n |= n >>> 2;
            n |= n >>> 4;
            n |= n >>> 8;
            n |= n >>> 16;
            return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
        }

    //通过 key值计算 hash。   
    // 从这里可以看出,key是可以为空的。 value自然没有要求
    static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }

    public V get(Object key) {
            Node<K,V> e;
            return (e = getNode(hash(key), key)) == null ? null : e.value;
        }

    final Node<K,V> getNode(int hash, Object key) {
            Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
            if ((tab = table) != null && (n = tab.length) > 0 &&
                //通过hash定位,如果有值,才进行冲突检测等后续查询
                (first = tab[(n - 1) & hash]) != null) {
                if (first.hash == hash && // always check first node
                    ((k = first.key) == key || (key != null && key.equals(k))))
                    return first;
                if ((e = first.next) != null) {
                    if (first instanceof TreeNode)
                        //如果是红黑树,则通过红黑树特有的方法进行获取节点
                        return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                    //如果是普通的链表,则直接往后循环即可
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            return e;
                    } while ((e = e.next) != null);
                }
            }
            return null;
        }

    public V put(K key, V value) {
            return putVal(hash(key), key, value, false, true);
        }

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
            Node<K,V>[] tab; Node<K,V> firstNode; int tableLength, i;
            if ((tab = table) == null || (tableLength = tab.length) == 0)
                tableLength = (tab = resize()).length;
            //通过hash定位,没有冲突的情况下,直接插入(node表)
            if ((firstNode = tab[i = (tableLength - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
            else {
                //存在冲突,则采用拉链法,或者红黑树处理冲突
                Node<K,V> e; K k;
                //如果hash冲突了,但是key相等,则直接覆盖
                if (firstNode.hash == hash &&
                    ((k = firstNode.key) == key || (key != null && key.equals(k))))
                    e = firstNode;
                //如果hash冲突了,但是key不等,并且当前节点为 红黑树节点,则调用红黑树进行插入
                else if (firstNode instanceof TreeNode)
                    e = ((TreeNode<K,V>)firstNode).putTreeVal(this, tab, hash, key, value);
                else {
                    //如果hash冲突了,先用拉链法,进行处理
                    for (int binCount = 0; ; ++binCount) {
                        if ((e = firstNode.next) == null) {
                            firstNode.next = newNode(hash, key, value, null);
                            //如果拉链法,到达了临界值,则采用红黑树
                            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                                treeifyBin(tab, hash);
                            break;
                        }
                        //如果在使用拉链法时,遇到了相同的节点,则跳出循环
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        //往链条尾部推进
                        firstNode = e;
                    }
                }
                //如果在拉链法过程中,遇到了相同的key,根据是否允许覆盖,从而做出具体的操作
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            ++modCount;
            //调整表格大小
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);
            return null;
        }

    //当单链表中的元素,有8个及以上的时候,则调整为红黑树
    final void treeifyBin(Node<K,V>[] tab, int hash) {
            int n, index; Node<K,V> firstNode;
            if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
                resize();
            else if ((firstNode = tab[index = (n - 1) & hash]) != null) {
                TreeNode<K,V> treeHead = null, tail = null;
                //将链表节点,转换为树节点,并将原有的单链表,组成双链表
                do {
                    TreeNode<K,V> curNode = replacementTreeNode(firstNode, null);
                    if (tail == null)
                        treeHead = curNode;
                    else {
                        curNode.prev = tail;
                        tail.next = curNode;
                    }
                    tail = curNode;
                } while ((firstNode = firstNode.next) != null);
                //调整红黑树
                if ((tab[index] = treeHead) != null)
                    treeHead.treeify(tab);
            }
        }

    final Node<K,V>[] resize() {
            //进行初始化,或者扩容为原来的2倍
            //扩容后,需要为原有的元素复制到新表
            //此处省略...
            return null;
        }

    }

# Hashtable

  • 抽象结构
public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {

    private transient Entry<?,?>[] table;

    private transient int count;

    private float loadFactor;

    public Hashtable(int initialCapacity, float loadFactor) {
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new IllegalArgumentException("Illegal Load: "+loadFactor);
    
            if (initialCapacity==0)
                initialCapacity = 1;
            this.loadFactor = loadFactor;
            table = new Entry<?,?>[initialCapacity];
            threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        }

    public synchronized V get(Object key) {
            Entry<?,?> tab[] = table;
            int hash = key.hashCode();
            int index = (hash & 0x7FFFFFFF) % tab.length;
            for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
                if ((e.hash == hash) && e.key.equals(key)) {
                    return (V)e.value;
                }
            }
            return null;
        }

    public synchronized V put(K key, V value) {
            // Make sure the value is not null
            if (value == null) {
                throw new NullPointerException();
            }
    
            // Makes sure the key is not already in the hashtable.
            Entry<?,?> tab[] = table;
            int hash = key.hashCode();
            int index = (hash & 0x7FFFFFFF) % tab.length;
            @SuppressWarnings("unchecked")
            Entry<K,V> entry = (Entry<K,V>)tab[index];
            for(; entry != null ; entry = entry.next) {
                if ((entry.hash == hash) && entry.key.equals(key)) {
                    V old = entry.value;
                    entry.value = value;
                    return old;
                }
            }
    
            addEntry(hash, key, value, index);
            return null;
        }

    private void addEntry(int hash, K key, V value, int index) {
            modCount++;
    
            Entry<?,?> tab[] = table;
            if (count >= threshold) {
                // Rehash the table if the threshold is exceeded
                rehash();
    
                tab = table;
                hash = key.hashCode();
                index = (hash & 0x7FFFFFFF) % tab.length;
            }
            Entry<K,V> e = (Entry<K,V>) tab[index];
            tab[index] = new Entry<>(hash, key, value, e);
            count++;
        }

}
   

# ConcurrentHashMap

  • 抽象结构
public interface ConcurrentMap<K, V> extends Map<K, V> {
    //...省略抽象
}
public abstract class AbstractMap<K,V> implements Map<K,V> {

    public V put(K key, V value) {
            throw new UnsupportedOperationException();
        }

    public abstract Set<Entry<K,V>> entrySet();

    public V get(Object key) {
            Iterator<Entry<K,V>> i = entrySet().iterator();
            if (key==null) {
                while (i.hasNext()) {
                    Entry<K,V> e = i.next();
                    if (e.getKey()==null)
                        return e.getValue();
                }
            } else {
                while (i.hasNext()) {
                    Entry<K,V> e = i.next();
                    if (key.equals(e.getKey()))
                        return e.getValue();
                }
            }
            return null;
        }

}


public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentMap<K,V>, Serializable {

    private static final sun.misc.Unsafe U;
    
    public V put(K key, V value) {
            return putVal(key, value, false);
        }

    final V putVal(K key, V value, boolean onlyIfAbsent) {
            if (key == null || value == null) throw new NullPointerException();
            int hash = spread(key.hashCode());
            int binCount = 0;
            for (Node<K,V>[] tab = table;;) {
                Node<K,V> firstNode; int n, currentHash, firstHash;
                //表未初始化,则先进行初始化
                if (tab == null || (n = tab.length) == 0)
                    tab = initTable();
                //没有存在hash冲突时,
                else if ((firstNode = tabAt(tab, currentHash = (n - 1) & hash)) == null) {
                    //使用原子操作,插入当前节点,这种操作是不需要加锁的
                    if (casTabAt(tab, currentHash, null,
                                 new Node<K,V>(hash, key, value, null)))
                        break;                   // no lock when adding to empty bin
                }
                else if ((firstHash = firstNode.hash) == MOVED)
                    tab = helpTransfer(tab, firstNode);
                else {
                    //存在hash冲突时
                    V oldVal = null;
                    //添加同步锁
                    synchronized (firstNode) {
                        if (tabAt(tab, currentHash) == firstNode) {
                            if (firstHash >= 0) {
                                binCount = 1;
                                //将当前节点加入链表
                                for (Node<K,V> e = firstNode;; ++binCount) {
                                    K ek;
                                    if (e.hash == hash &&
                                        ((ek = e.key) == key ||
                                         (ek != null && key.equals(ek)))) {
                                        oldVal = e.val;
                                        if (!onlyIfAbsent)
                                            e.val = value;
                                        break;
                                    }
                                    Node<K,V> pred = e;
                                    if ((e = e.next) == null) {
                                        pred.next = new Node<K,V>(hash, key,
                                                                  value, null);
                                        break;
                                    }
                                }
                            }
                            //如果当前链表为红黑树,则采用红黑树的方法插入节点
                            else if (firstNode instanceof TreeBin) {
                                Node<K,V> p;
                                binCount = 2;
                                if ((p = ((TreeBin<K,V>)firstNode).putTreeVal(hash, key,
                                                               value)) != null) {
                                    oldVal = p.val;
                                    if (!onlyIfAbsent)
                                        p.val = value;
                                }
                            }
                        }
                    }
                    if (binCount != 0) {
                        //如果链表超过了指定长度,则调整为红黑树
                        if (binCount >= TREEIFY_THRESHOLD)
                            treeifyBin(tab, currentHash);
                        if (oldVal != null)
                            return oldVal;
                        break;
                    }
                }
            }
            addCount(1L, binCount);
            return null;
        }

     public V get(Object key) {
            //获取节点时,未进行加锁
            Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
            int h = spread(key.hashCode());
            if ((tab = table) != null && (n = tab.length) > 0 &&
                (e = tabAt(tab, (n - 1) & h)) != null) {
                if ((eh = e.hash) == h) {
                    if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                        return e.val;
                }
                else if (eh < 0)
                    return (p = e.find(h, key)) != null ? p.val : null;
                while ((e = e.next) != null) {
                    if (e.hash == h &&
                        ((ek = e.key) == key || (ek != null && key.equals(ek))))
                        return e.val;
                }
            }
            return null;
        }

    static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                            Node<K,V> c, Node<K,V> v) {
            return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
        }

    }

# HashMap与Hashtable区别

  • 线程安全性: Hashtable是线程安全的,HashMap不是
  • 效率: HashMap的效率更高
  • null key 与 null value的支持: HashMap支持null Key以及null Value; Hashtable 不支持null Key与null Value
  • 初始容量与扩容大小不同:
    • 初始大小: Hashtable直接使用指定的大小; HashMap扩充为最小的2的幂次方大小;
    • 初始时不指定大小时: Hashtable默认初始大小11,扩容为2n+1; HashMap默认16,扩容为2n;
  • 底层数据结构不同: Hashtable采用数组+链表(拉链法解决hash冲突); HashMap采用数组+链表实现(链表可以调整为红黑树,拉链法解决hash冲突);

# HashMap 与 HashSet区别

  • hashCode值的计算: HashMap通过键计算; HashSet通过 成员对象 计算;
class HashSet{

    private static final Object PRESENT = new Object();

    public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }
}

class HashMap{
    public V put(K key, V value) {
            return putVal(hash(key), key, value, false, true);
        }
}

# HashMap的底层实现

  • JDK8以前: 数组+链表
  • JDK8以后: 数组+链表+红黑树

# HashMap的长度为什么是2的幂次方

  • 运算效率: 取余操作,使用二进制&操作,具有更高的运算效率;

# HashMap的遍历方式

  • 迭代器遍历
  • forEach方法遍历
  • lambda方式遍历
  • Streams方式遍历

# ConcurrentHashMap 与 Hashtable 的区别

  • 底层数据结构: ConcurrentHashMap采用 数组+链表+红黑树(java8及以后); Hashtable采用数组+链表实现;
  • 实现线程安全的方式:
    • ConcurrentHashMap:
      1. java7对数组的不同部分进行分割,每一把锁,只锁容器的一部分数据;
      2. java8直接使用 数组+链表+红黑树结构实现,并发控制通过 方法内部synchronized和原子操作实现;
    • Hashtable: 全表使用同一把锁来保证线程安全,效率非常低下;

# ConcurrentHashMap的具体实现方式:

  • JDK7:
    1. 一个ConcurrentHashMap里面包含一个 Segment数组,是一个可重入锁;
    2. Segment的结构也是数组+链表
  • JDK8:
    1. synchronize 之锁定当前链表或者红黑树的首节点;
    2. 在hash没有冲突的情况下,就不会产生并发,效率可提升N倍;

# HashMap疑难篇

# 底层数据结构

  • java7: 数组+链表
  • java8: 数组+链表+红黑树
    • 红黑树的条件
      1. 链表长度大于阈值(8) 并且 数组长度超过64;

# 存值过程

  • java7:
    1. key的hashCode经过扰动函数后,得到hash值
    2. (n-1)&hash,判断元素存放位置
    3. 如果存在元素,判断该元素的key是否与即将插入的key相同;
    4. 相同则进行覆盖,不同则采用拉链法进行解决(头插法);
  • java8:
    1. 前三步一样
    2. 相同则覆盖,不同则新增节点。如果原有节点是树节点,则直接通过红黑树插入,否则通过拉链法插入;
    3. 拉链法插入后,需要判断是否满足转为红黑树的条件,满足则转为红黑树,不满足则进行扩容;

# 构造方法

  • 空参构造
  • 包含另一个Map的构造
  • 包含 初始数组大小的构造
  • 包含 容量大小 和加载因子 的构造;

# 主要方法

  • put方法
  • get方法
  • resize方法: 会伴随一次重新hash分配,会遍历表中的所有元素,非常耗时; 编写程序时,应该尽量避免;

# 扩容条件

  • java7:
    • 发送hash冲突
    • 或者表中元素超过了 初始阈值(16)
  • java8:
    • 转换二叉树前,都会进行扩容;

# ConcurrentHashMap疑难篇

# 底层结构

  • java7: Segment数组+HashEntry数组+链表
    • Segment数组,不可变,默认16;
    • 每个Segment管理一个 HashMap结构;
  • java8:
    • Node数组+链表、红黑树

# 构造函数

  • java7: 空参构造,内部调用有参构造。
    • 初始化容量
    • 负载因子
    • 默认并发级别
  • java8: 空参构造;

# 存值过程

  • java7:
    1. 计算要push的key的位置,获取指定位置的Segment
    2. 指定位置的 Segment为空,则初始化
    3. 调用 Segment.put插入
  • java8: 1.根据key计算出 hashCode 2.找到key定位的Node,为空则表示可以写入,利用 CAS尝试写入,失败则自旋保证成功 3. 有冲突,则勇synchronized锁写入数据

# 容器疑难

# 数组转换为ArrayList的方法

  1. 通过Arrays工具类: new ArrayList(Arrays.asList("a","b","c"));
  2. java8的stream语法: Arrays.stream(myArray).collect(Collectors.toList());
  3. 使用guava: ImmutableList.of(myArray) 或 ImmutableList.copyOf(myArray)

# 数组的编辑操作

  • 不能再 forEach循环里 进行集合元素的 remove/add 操作;
  • 使用迭代器进行删除、新增操作
  • java8开始支持 removeIf()操作