Java Collection

3、  Java Collection

 

1.  Collection 接口

// Collection 接口在其源碼中的定義:
 * 1. Collection 是集合層次結構中的根接口 * 2. 一個 Collection 表明 一組對象(objects),這些對象稱爲其元素(elements)
 * 3. 一些 Collection 容許有重複的元素,而另外一些 Collection 不容許有重複的元素
 * 4. 一些 Collection 的元素是有序的,而另外一些 Collection 是無序的
 * 5. JDK 沒有提供任何直接(direct)實現 Collection 的類
 * 6. JDK 只提供了繼承 Collection 的「子接口」(List 和 Set)的實現類

 * -Set 中的元素沒有順序且不能夠重複
 * -List 中的元素有順序且能夠重複
 * -Map 接口定義了存儲 「鍵(key)- 值(value)映射對」 的方法,鍵(key)不能重複,重寫 hashCode() 和 equals() 方法!
 * 
 *      * 接口             Collection
 *                        /        \
 *      * 接口           Set        List               Map
 *                       |        /     \              |
 *      * 類         HashSet  ArrayList LinkedList    HashMap

// 談到 Collection 接口,咱們不得不區別一下 Collections 類
// Collections 是一個類,不能實例化,是一個工具類,它包含各類有關集合操做的靜態(static)多態方法,好比 sort、search、shuffle 以及線程安全等操做
public class Collections {
    // 私有化構造器,因此不能被實例化
    private Collections() {
    }
    ...
}

 

2.  ArrayList

1. ArrayList 是 List 接口的實現類,並繼承於 AbstractList(AbstractList 繼承於 AbstractCollection,而且實現了大部分 List 接口)
2. ArrayList 的底層實現是動態數組(屬於數據結構中的可擴容的線性表),線程不安全,效率高 // 建立
List list = new ArrayList();

// 經常使用方法
list.size();// 返回元素的數量
list.isEmpty();
list.add(element);
list.add(index, element);// 在指定位置添加元素
list.remove(index);// 刪除指定索引的元素,並返回該元素內容
list.remove(element);// 刪除指定元素,刪除成功返回 true
list.get(index);// 獲取指定位置的元素
list.toArray();// 返回一個包含列表中的全部元素的數組
list.contains(element);// 判斷是否包含指定元素
list.indexOf(element);// 返回指定元素所在的第一個位置,沒找到返回 -1
list.lastIndexOf(element);// 返回指定元素所在的最後一個位置
list.clear();// 刪除全部元素

// 爲了更深刻了解 ArrayList,咱們來分析一下 ArrayList 的部分源碼  
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final int DEFAULT_CAPACITY = 10;// 默認容量爲 10
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 最大容量
    private static final Object[] EMPTY_ELEMENTDATA = {};
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    transient Object[] elementData;// 可見底層是 Object 對象數組
    private int size;// 包含元素的數量

    // 無參構造器,對象數組默認爲空
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    // 有參構造器,用於初始化數組容量
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
        }
    }

    // 返回所包含元素的數量
    public int size() {
        return size;
    }

    // 判斷對象數組是否爲空
    public boolean isEmpty() {
        return size == 0;
    }

    // 返回一個包含列表中的全部元素的數組
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
    
    // 刪除元素
    public E remove(int index) {
        rangeCheck(index);
        modCount++;
        E oldValue = elementData(index);
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index, numMoved);
        elementData[--size] = null;
        return oldValue;
    }

    // 添加元素
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);// 判斷是否須要擴容
        elementData[size++] = e;
        return true;
    }

    // 判斷是否須要擴容
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    // 擴容方法
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        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);
    }
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0)
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }
    ...
 } 
ArrayList

 

3.  LinkedList

1. LinkedList 是 List 接口的實現類,並繼承於 AbstractSequentialList(AbstractSequentialList 繼承於 AbstractList,提供了對數據元素的鏈式訪問而不是隨機訪問)
2. LinkedList 的底層實現是鏈表(屬於數據結構中的雙向鏈表),線程不安全,效率高 // 建立
List link = new LinkedList();

// 經常使用方法
link.size();
link.getFirst();// 返回第一個節點的元素
link.getLast();// 返回最後一個節點的元素
link.get(index);// 返回指定位置的元素
link.contain(element);// 判斷鏈表中是否有指定元素
link.addFirst(element);// 從鏈表頭部插入元素,無返回值
link.add(element); // 從鏈表尾部插入元素,插入成功返回 true
link.add(index, element);// 在指定位置插入元素,無返回值
link.remove(element); // 刪除指定元素,刪除成功返回 true
link.remove(index);// 刪除指定位置的元素,刪除成功返回被刪除元素
link.addAll(Collection);// 添加指定集合
link.clear();// 清空鏈表,釋放節點空間

// 爲了更深刻了解 LinkedList,咱們來分析一下 LinkedList 的部分源碼
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{   
    // 用私有靜態內部類定義鏈表的節點對象
    private static class Node<E> {
        E item; // 存放元素內容
        Node<E> next;// 指向下一個節點
        Node<E> prev;// 指向前一個節點
        //有參構造器
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

    transient int size = 0;// 元素的數量
    transient Node<E> first;// 指向鏈表的第一個節點
    transient Node<E> last;// 指向鏈表的最後一個節點
   
    public LinkedList() {
    }

    // 有參構造器,用於構造包含指定集合的鏈表
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);// 添加指定集合
    }
    // 返回元素的數量
    public int size() {
        return size;
    }

    // 在鏈表的頭部插入元素
    public void addFirst(E e) {
        linkFirst(e);
    }
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

    // 在鏈表的尾部插入元素
    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 boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> 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;
    }
    
    // 清空鏈表
    public void clear() {
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }
}
LinkedList

 

4.  HashMap

1. HashMap 是 Map 接口的實現類,並繼承於 AbstractMap(AbstractMap 是 Map 接口的抽象實現類,而且實現了大部分 Map 接口)
2. HashMap 的底層實現:哈希表+鏈表(屬於數據結構中的鄰接表)

// 建立
Map map = new HashMap();

// 經常使用方法
map.put(k,v);// 添加鍵值對,若是 key 已存在,則覆蓋 value,並返回被覆蓋的 value;不存在,則返回 null
map.putAll(map02);// 添加指定的 map 集合
map.remove(k);// 刪除指定 key 的節點,並返回 value
map.get(k);// 獲取指定 key 的 value
map.containsKey(k);// 判斷是否含有指定 key 的節點
map.containsValue(v);// 判斷是否含有指定 value 的節點
map.size();// 返回鍵值對的數量
map.values();//獲取全部 value,返回值類型 Collection<valueType>
map.keySet(); //獲取全部 key,返回值類型 Set<keyType>
map.entrySet();// 獲取全部 key 和 value,返回值類型 Set<Entry<keyType, valueType>>
map.clear();// 清空 map 裏的全部鍵值對

// 爲了更深刻了解 HashMap,咱們來分析一下 HashMap 的部分源碼
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;// 默認最小容量 MUST be a power of two. aka 16
    static final int MAXIMUM_CAPACITY = 1 << 30;// 默認最大容量
    static final float DEFAULT_LOAD_FACTOR = 0.75f;// 默認負載因子

    // 用靜態內部類定義鏈表的節點對象
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;// 存放 hashCode
        final K key;// 存放 key
        V value; // 存放 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 K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }
        
        /* 使用本身定義的 hashCode()方法
           這裏給出 java.util.Objects 中定義的 hashCode()方法
           public static int hashCode(Object o) {
               return o != null ? o.hashCode() : 0;
           }
        */
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
         
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        /* 使用本身定義的 equals()方法
           這裏給出 java.util.Objects 中定義的 equals()方法
           public static boolean equals(Object a, Object b) {
               return (a == b) || (a != null && a.equals(b));
           }
        */ 
        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }
 
    transient Node<K,V>[] table;// 聲明哈希表
    transient int size;// 元素的數量
    final float loadFactor;// 負載因子
    
    // 無參構造器
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
    }
    
    public int size() {
        return size;
    }
    
    public boolean isEmpty() {
        return size == 0;
    }
    
    // 定義了本身的 hash()方法
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    /* 添加鍵值對
       咱們先在這給出 java.lang.Object 的 equals()方法
       public boolean equals(Object obj) {
           return (this == obj);
       }
       因此,咱們在作 key 的比較時,要根據本身的需求重寫 equals()方法 
    */
    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> p;
        int n, i;
        // 若是哈希表爲空,初始化哈希表
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        // 若是 hash(key) 對應哈希表位置爲 null,直接存放該節點
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            // 若是 hash(key) 對應哈希表位置存放的節點 key 等於 咱們要添加的鍵值對的 key,則直接覆蓋 value
            if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            // 若是是樹節點的操做
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            // 若是 hash(key) 對應哈希表位置存放的節點 key 不等於 咱們要添加的鍵值對的 key,則往下尋找,若是有相等的,覆蓋 value,沒有則在最後一個節點後添加新節點存放
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.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;
                    p = e;
                }
            }
            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;
    }
    
    // 有參構造器,使用與指定的 Map 相同的映射,構造一個新的 HashMap,新的 HashMap 內包含了指定 map 的全部元素
    public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

    // 獲取指定 key 的 value
    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 &&
            (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;
    }
    
    // 是否存在指定 key
    public boolean containsKey(Object key) { 
        return getNode(hash(key), key) != null; 
    }
    
    // 刪除指定 key 的鍵值對,並返回 value
    public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value;
    }
    final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        Node<K,V>[] tab; 
        Node<K,V> p; 
        int n, index;
        if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) {
            Node<K,V> node = null, e; 
            K k; 
            V v;
            if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;
            else if ((e = p.next) != null) {
                if (p instanceof TreeNode)
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                else {
                    do {
                        if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) {
                if (node instanceof TreeNode)
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                else if (node == p)
                    tab[index] = node.next;
                else
                    p.next = node.next;
                ++modCount;
                --size;
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }
    
    // 清空
    public void clear() {
        Node<K,V>[] tab;
        modCount++;
        if ((tab = table) != null && size > 0) {
            size = 0;
            for (int i = 0; i < tab.length; ++i)
                tab[i] = null;
        }
    }
}
HashMap

 

5.  HashSet

1. HashSet 是 Set 接口的實現類,並繼承於 AbstractSet(AbstractSet 繼承於 AbstractCollection,而且實現了大部分 Set 接口)
2. HashSet 的底層實現:利用 「HashMap 鍵惟一」 的特性

// 建立
Set set = new HashSet();

// 經常使用方法
set.size();// 返回元素的數量
set.isEmpty();
set.add(e);// 添加元素,添加成功返回 true,不然返回 false
set.remove(e);// 刪除指定元素,刪除成功返回 true
set.contains(e);// 判斷指定元素是否已存在
set.clear();// 清空 set 裏的全部元素

// 爲了更深刻了解 HashSet,咱們來分析一下 HashSet 的部分源碼
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    private transient HashMap<E,Object> map;// 哇,直接就用了 HashMap

    private static final Object PRESENT = new Object();// 建立一個 Object 對象常量,其實就是用來填充 HashMap 的 value

    public HashSet() {
        map = new HashMap<>();
    }

    public int size() {
        return map.size();
    }

    public boolean isEmpty() {
        return map.isEmpty();
    }

    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    public boolean add(E e) {
        // 當添加元素 e 已經存在時,即對應的 map 裏的 key 存在,則 map 的 put()方法會返回被覆蓋的 value,這個 value 就是 PRESENT,不等於 null,故返回 false
        return map.put(e, PRESENT)==null;
    }

    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    public void clear() {
        map.clear();
    }
}
HashSet

 

6.  Iterator 迭代器

1.全部實現了 Collection 接口的容器類都有一個 iterator() 方法,用來返回一個實現了 Iterator 接口的對象
2.迭代器使遍歷操做更加方便
3.java.util.Iterator 接口定義了以下方法:
 * boolean hasNext();// 判斷是否有下一個節點
 * Object next();// 返回遊標(cursor)當前位置的元素,而且遊標(cursor)指向下一個位置
 * void remove();// 刪除遊標左邊(lastRet)的元素,在執行完 next()方法 以後該操做只能執行一次,由於每次執行 remove()方法後 lastRet 都會置爲 -1;只有當再次使用 next()方法後,纔會給 lastRet 從新賦值

// 下面給出 ArrayList 的遍歷操做,LinkedList、HashMap、HashSet 的遍歷操做都相似,須要注意的是 Map 和 Set 是用 哈希表+鏈表 實現的,因此不能使用第一種遍歷方式(索引遍歷)
public class Traversal {
    public static void main(String[] args) {
        List<String>  list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        
        //遍歷方式一
        for(int i=0;i<list.size();i++)
            System.out.println(list.get(i));
        
        //遍歷方式二
        for(String li : list)
            System.out.println(li);
        
        //遍歷方式三
        for(Iterator<String> iterator = list.iterator();iterator.hasNext();)
            System.out.println(iterator.next());
        
        //遍歷方式四
        Iterator<String> iterator = list.iterator();
        while(iterator.hasNext())
            System.out.println(iterator.next());
    }
}