Java集合

单列集合 (Collection)

单列集合类的根接口,用于:存储一系列符合某种规则的元素。

Collection继承关系

List 集合

有序,可重复

一、ArrayList

底层数组实现

特点:有序,可重复、非线程安全、查询快,删除慢(需要移动元素)

  1. ArrayList 类中的全局变量

     private static final int DEFAULT_CAPACITY = 10;  //默认初始容量大小10
     transient Object[] elementData; //底层数组实现
     private int size;    //实际元素个数
    
  2. ArrayList 扩容机制【默认10,每次扩容是原来的1.5倍】

     private void grow(int minCapacity) {
            //获取原数组大小
            int oldCapacity = elementData.length;
            //新数组大小 = 原数组大小 + 原数组大小右移一位(右1移相当于除以2) 
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // 将原数组的值拷贝到新数组,并将新数组赋值给变量
            elementData = Arrays.copyOf(elementData, newCapacity);
     }
    
  3. ArrayList 新增方法

    1. 未指定位置,追加到后面

      public boolean add(E e) {
          ensureCapacityInternal(size + 1);  // Increments modCount!!
          elementData[size++] = e;
          return true;
      }
      
      1. 指定位置插入
      public void add(int index, E element) {
          rangeCheckForAdd(index);
          ensureCapacityInternal(size + 1);
          //如果在中间插入,元素后移,空出位置
          System.arraycopy(elementData, index, elementData, index + 1, size - index);
          elementData[index] = element;
          size++;
      }
      

二、LinkedList

底层双向链表实现 ,

特点:有序,可重复,非线程安全、适用于增删操作

  1. LinkedList 类中的全局变量

     transient int size = 0;   //初始大小0
     transient Node first;  //头节点
     transient Node last;   //尾节点
    
  1. Node为LinkedList的内部类,由此我们可知道 LinkedList 为双向连接结构

    private static class Node {
        E item;
        Node next;
        Node prev;
    }
    
    Node(Node prev, E element, Node next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
    
  1. LinkedList 新增元素

    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    
    void linkLast(E e) {
        final Node l = last;
        final Node newNode = new Node<>(l, e, null);
        last = newNode;   //更换尾节点
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    
  1. LinkedList 删除元素

    public boolean remove(Object o) {
        //遍历删除
        if (o == null) {
            for (Node x = first; x != null; x = x.next) { 
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
    
    //删除元素
    E unlink(Node x) {
        // assert x != null;
        final E element = x.item;
        final Node next = x.next;
        final Node prev = x.prev;
    
        if (prev == null) { //如果前面没有节点,就把下一个节点赋值给头节点
            first = next;
        } else {//如果前面存在节点,则把前节点指向我的下一节点,置空我的前节点
            prev.next = next;
            x.prev = null;
        }
    
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }
    
        x.item = null;
        size--;
        modCount++;
        return element;
    }
    

三、Vector

查看源代码,会发现每个方法是使用 Synchronized 关键字修饰

Vector与ArrayList,一样底层数组实现

特点:有序,可重复、线程安全

  1. Vector 默认初始大小 10

    public Vector() {
       this(10);
    }
    
  2. Vector 新增方法 (synchronized 保证线程安全)

    public synchronized boolean add(E e) {
       modCount++;
       ensureCapacityHelper(elementCount + 1);
       elementData[elementCount++] = e;
       return true;
    }
    
  3. Vector 扩容机制 【2倍扩容】

    private void grow(int minCapacity) {
        // 获取当前数组大小
        int oldCapacity = elementData.length;
        // capacityIncrement 默认是 0 ,所以新数组长度 = oldCapacity + oldCapacity
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                       capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    
    //capacityIncrement 默认是 0 
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }
    
    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }
    

Set无序集合

无序,不重复

一、HashSet

基于 HashMap 实现的,底层采用 HashMap 来保存元素

特点:无序、不重复,非线程安全。

  1. HashSet 类中的全局变量

    private transient HashMap map;  //存储数据
    private static final Object PRESENT = new Object(); //与后台映射中的对象关联的虚拟值
    
  2. 构造函数 【初始化一个 HashMap 存储数据】

    public HashSet() {
        map = new HashMap<>();
    }
    
  3. HashSet 新增元素 【可以看出,使用的 Map 存储的】

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    
  4. HashSet 删除元素

    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
    
  5. HashSet 只能通过迭代器取出元素,因为没有提供 get 方法。

    HashSet set = new HashSet();
    Iterator iterator = set.iterator();
    while (iterator.hasNext()) {
        Object next = iterator.next();
        System.out.println(next);
    }
    

二、TreeSet

特点:有序,不重复

  1. TreeSet 类中的全局变量

    private transient NavigableMap m;  //TreeMap 的父类是 NavigableMap
    private static final Object PRESENT = new Object();
    
  2. TreeSet 构造函数,可见底层使用 TreeMap 存储数据。【TreeMap的实现是红黑树算法的实现】

    public TreeSet() {
        this(new TreeMap());
    }
    

双列集合 (Map)

双列集合类的根接口,用于:存储具有键(Key)、值(Value)映射关系的元素。

Map继承关系

一、HashMap

底层:数组 + 链表 + 红黑树

特点:可序列化,线程不安全,key、value可以为空

  1. HashMap 全局变量

    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //16 默认的初始容量-必须是2的幂
    
    static final int MAXIMUM_CAPACITY = 1 << 30; //最大容量 10 7374 1824
    
    static final float DEFAULT_LOAD_FACTOR = 0.75f; //默认的加载因子
    
    static final int TREEIFY_THRESHOLD = 8; //由链表转化为树的阈值
    
    static final int UNTREEIFY_THRESHOLD = 6; //由树转换成链表的阈值
    
    //最小树形化阈值 ,即 当哈希表中的容量 > 该值时,才允许树形化链表 (即 将链表 转换成红黑树)
    static final int MIN_TREEIFY_CAPACITY = 64; 
    
    //---------- Fields ----------
    
    transient Node[] table; //存储元素的数组
    
    transient Set> entrySet;
    
    transient int size; //元素的个数
    
    transient int modCount; // 每次扩容和更改map结构的计数器
    
    int threshold; //临界值 当实际大小(容量*负载因子)超过临界值时,会进行扩容
    
    final float loadFactor; //加载因子
    
  2. HashMap 构造函数

    /**
     * Constructs an empty HashMap with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    
    /**
     * Constructs an empty HashMap with the specified initial
     * capacity and the default load factor (0.75).
     *
     * @param  initialCapacity the initial capacity.
     * @throws IllegalArgumentException if the initial capacity is negative.
     */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    
    
    /**
     * Constructs an empty HashMap with the specified initial
     * capacity and load factor.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    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);
    }
    
  1. HashMap 计算扩容的阈值

    /**
     * Returns a power of two size for the given target capacity.
     * 返回给定目标容量的2次幂大小。
     */
    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;
    }
    
  1. HashMap 计算 hash 值

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
  2. HashMap 新增元素

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    
    /**
     * onlyIfAbsent if true, don't change existing value
     * evict if false, the table is in creation mode.
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
        Node[] tab; Node p; int n, i;
        //首次进入 table为 null 初始化数组
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //计算位置,该位置为空 直接赋值
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null); 
        //不为空的情况
        else {
            Node e; K k;
            //判断 key 值是否相等
            if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //判断当前是否为红黑树
            else if (p instanceof TreeNode) 
                e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
            //当 key 值不同、不属于树结构,作为链表节点插入
            else {
                for (int binCount = 0; ; ++binCount) {
                    //当遍历的节点,next 值为空,插入到此位置
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //如果链表长度达到阈值,转化为红黑树
                        if (binCount >= TREEIFY_THRESHOLD - 1)
                            treeifyBin(tab, hash);
                        break;
                    }
                    //当 key 值存在相同的,结束循环,不进行插入
                    if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            //当 e != null 说明链表中存在相同的 key 
            if (e != null) {
                V oldValue = e.value;
                // onlyIfAbsent 默认 false ,覆盖原来 value 的值
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value; 
                afterNodeAccess(e);
                return oldValue;
            }
        }
        //增加修改次数
        ++modCount;
        //如果 ++size 大于临界值,进行扩容
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    
    
  3. HashMap 扩容代码【二倍扩容】

    /**
     * Initializes or doubles table size.  If null, allocates in
     * accord with initial capacity target held in field threshold.
     * Otherwise, because we are using power-of-two expansion, the
     * elements from each bin must either stay at same index, or move
     * with a power of two offset in the new table.
     * 如果为空,则按照字段阈值中持有的初始容量目标进行分配
     * 否则,我们使用2的幂展开
     * @return the table
     */
    final Node[] resize() {
        Node[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        //当前数组不为空的情况下
        if (oldCap > 0) {
            //如果查过最大容量 1<<30 ,则当前的临界值设为最大整型值
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            //如果原来长度左移1位(相当于乘2)小于最大容量 并且 大于默认大小 16 的情况下,双倍扩容
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        //使用带有初始容量的构造器时,table容量为初始化得到的threshold
        else if (oldThr > 0) 
            newCap = oldThr;
        //默认构造器下进行扩容  
        else {      
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        //使用带有初始容量的构造器在此处进行扩容
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node[] newTab = (Node[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    //元素后面没有值的情况,计算在新数组的位置进行赋值
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    //元素后面是红黑树的情况下,移动元素
                    else if (e instanceof TreeNode)
                        ((TreeNode)e).split(this, newTab, j, oldCap);
                    //其他情况,元素后面是链表的情况下,移动元素
                    else { // preserve order
                        Node loHead = null, loTail = null;
                        Node hiHead = null, hiTail = null;
                        Node next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
    
  1. HashMap 获取元素

    public V get(Object key) {
        Node e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
    
    //获取元素
    final Node getNode(int hash, Object key) {
        Node[] tab; Node first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            //key 是否等于 头节点 key
            if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                //从红黑树中查找
                if (first instanceof TreeNode)
                    return ((TreeNode)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;
    }
    

二、Hashtable

底层:数组 + 链表

特点:不允许使用 null 作为 key 或 value 。线程安全

  1. Hashtable 全局变量

    private transient Entry[] table;  //存放数据
    private transient int count; //元素个数
    private int threshold; //当表的大小超过这个阈值时,扩容
    private float loadFactor; //加载因子
    private transient int modCount = 0; //修改次数
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //数组最大大小
    
  2. Hashtable 构造函数

    /**
     * Constructs a new, empty hashtable with a default initial capacity (11) and load factor (0.75).
     * 无参构造,默认数组大小11,加载因子 0.75
     */
    public Hashtable() {
        this(11, 0.75f);
    }
    
    /**
     * 指定了大小,默认加载因子 0.75
     */
    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }
    
    /**
     * Constructs a new, empty hashtable with the specified initial
     * capacity and the specified load factor.
     *
     * @param      initialCapacity   the initial capacity of the hashtable.
     * @param      loadFactor        the load factor of the hashtable.
     * @exception  IllegalArgumentException  if the initial capacity is less
     *             than zero, or if the load factor is nonpositive.
     * 指定大小、加载因子
     */
    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);
    }
    
    
  1. Hashtable 新增元素

    public synchronized V put(K key, V value) {
        // value 为空的情况下,抛出异常
        if (value == null) {
            throw new NullPointerException();
        }
        // Makes sure the key is not already in the hashtable.
        Entry tab[] = table;
        //当 key 为空的情况下,会抛出空指针异常
        int hash = key.hashCode();
        //计算该元素应放置的位置
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry entry = (Entry)tab[index];
        //如果该位置存在元素
        for(; entry != null ; entry = entry.next) {
            //如果 key 值相同,将新值赋值给元素
            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;
        }
        // Creates the new entry.
        @SuppressWarnings("unchecked")
        Entry e = (Entry) tab[index];
        //将新结点插到链表首部  
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }
    
  2. Hashtable扩容 【二倍加1】

    protected void rehash() {
        int oldCapacity = table.length;
        Entry[] oldMap = table;
        // 原大小左移一位 加 1
        int newCapacity = (oldCapacity << 1) + 1;
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }
        Entry[] newMap = new Entry[newCapacity];
        modCount++;
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        table = newMap;
        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry old = (Entry)oldMap[i] ; old != null ; ) {
                Entry e = old;
                old = old.next;
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = (Entry)newMap[index];
                newMap[index] = e;
            }
        }
    }
    
  3. Hashtable 获取元素

    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;
    }
    

Collections类

怎么实现线程安全?

//List集合实现线程安全
List l1 = new ArrayList();
List l2 = Collections.synchronizedList(l1); 

//Set集合实现线程安全
Set s1 = new HashSet();
Set s2 = Collections.synchronizedSet(s1);

//Map 实现线程安全
Map m1 = new HashMap();
Map m2 = Collections.synchronizedMap(m1);

你可能感兴趣的:(Java集合)