先看圖java
從類圖結構能夠了解 java.util包下的2個大類:node
一、Collecton:能夠理解爲主要存放的是單個對象數組
二、Map:能夠理解爲主要存儲key-value類型的對象安全
1、Collection數據結構
Collection繼承了Iterate接口,Iterate用於集合內迭代器抽象接口,其子類均實現接口中方法,看下ArrayList下實現:app
1 /** 2 * Returns an iterator over the elements in this list in proper sequence. 3 * 4 * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>. 5 * 6 * @return an iterator over the elements in this list in proper sequence 7 */ 8 public Iterator<E> iterator() { 9 return new Itr(); // 返回內部類實例 10 } 11 12 /** 13 * An optimized version of AbstractList.Itr 14 */ 15 private class Itr implements Iterator<E> { 16 int cursor; // index of next element to return 指向下一個位置索引id 17 int lastRet = -1; // index of last element returned; -1 if no such 指向上一個位置索引id 18 int expectedModCount = modCount; 19 20 public boolean hasNext() { 21 return cursor != size; 22 } 23 24 @SuppressWarnings("unchecked") 25 public E next() { 26 checkForComodification(); 27 int i = cursor; 28 if (i >= size) 29 throw new NoSuchElementException(); 30 Object[] elementData = ArrayList.this.elementData; 31 if (i >= elementData.length) 32 throw new ConcurrentModificationException(); 33 cursor = i + 1; 34 return (E) elementData[lastRet = i]; 35 } 36 37 public void remove() { 38 if (lastRet < 0) 39 throw new IllegalStateException(); 40 checkForComodification(); 41 42 try { 43 ArrayList.this.remove(lastRet); 44 cursor = lastRet; 45 lastRet = -1; 46 expectedModCount = modCount; 47 } catch (IndexOutOfBoundsException ex) { 48 throw new ConcurrentModificationException(); 49 } 50 } 51 52 @Override 53 @SuppressWarnings("unchecked") 54 public void forEachRemaining(Consumer<? super E> consumer) { 55 Objects.requireNonNull(consumer); 56 final int size = ArrayList.this.size; 57 int i = cursor; 58 if (i >= size) { 59 return; 60 } 61 final Object[] elementData = ArrayList.this.elementData; 62 if (i >= elementData.length) { 63 throw new ConcurrentModificationException(); 64 } 65 while (i != size && modCount == expectedModCount) { 66 consumer.accept((E) elementData[i++]); 67 } 68 // update once at end of iteration to reduce heap write traffic 69 cursor = i; 70 lastRet = i - 1; 71 checkForComodification(); 72 } 73 74 final void checkForComodification() { 75 if (modCount != expectedModCount) 76 throw new ConcurrentModificationException(); 77 } 78 }
一、Listide
特色:有序結果、順序遍歷、索引、容許有重複值性能
(1) ArrayList ui
以上特色實現:this
transient Object[] elementData; // List內部存儲對象數組結果 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } // 添加對象時先識別是否越界,沒有越界則數組對象當前索引值的下一個添 // 添加對象時,不識別重複,因此有序容許重複值 /** * Removes all of the elements from this list. The list will * be empty after this call returns. */ public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; } // 清空List時順序遍歷值置爲null
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
其中 remove方法 :
public E remove(int index) { // 按索引刪除對象 rangeCheck(index); // 校驗輸入索引id是否越界,若越界則拋出運行時異常 IndexOutOfBoundsException modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; // 定位到索引的下一位 if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); //調用native方法實現數組位置左移 elementData[--size] = null; // clear to let GC do its work // 末尾元素置空 return oldValue; } public boolean remove(Object o) {// 按對象刪除 if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { // 識別對象相等使用equals方法,使用時注意重寫equals方法 fastRemove(index); return true; } } return false; } /* * Private remove method that skips bounds checking and does not * return the value removed. */ private void fastRemove(int index) { modCount++; // 刪除元素時,modCount值變動 int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }
能夠看到ArrayList中對數組進行,操做時經常使用到System.arraycopy
java.lang.System下
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
還有在 java.util.Arrays下數組copy方法,最終也是調用System.arraycopy方法
1 public static <T> T[] copyOf(T[] original, int newLength) { 2 return (T[]) copyOf(original, newLength, original.getClass()); 3 } 4 5 public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { 6 @SuppressWarnings("unchecked") 7 T[] copy = ((Object)newType == (Object)Object[].class) 8 ? (T[]) new Object[newLength] 9 : (T[]) Array.newInstance(newType.getComponentType(), newLength); 10 System.arraycopy(original, 0, copy, 0, 11 Math.min(original.length, newLength)); 12 return copy; 13 }
示例:
1 package jdk.array; 2 3 import java.util.ArrayList; 4 import java.util.Arrays; 5 import java.util.Iterator; 6 7 public class ArrayListTest1 { 8 9 public static void main(String[] args) { 10 11 ArrayList<String> l1 = new ArrayList<>(); 12 l1.add("s1"); 13 l1.add("s2"); 14 l1.add("s1"); 15 l1.add("s2"); 16 l1.add("s2"); 17 l1.add("s2"); 18 l1.add("s3"); 19 l1.add("s3"); 20 l1.add("s3"); 21 22 // 使用容器迭代器遍歷List 23 Iterator<String> iterator = l1.iterator(); 24 while (iterator.hasNext()) { 25 String str = iterator.next(); 26 if ("s1".equals(str)) { 27 iterator.remove(); // 迭代器內部方法remove() 28 } 29 } 30 System.out.println(Arrays.toString(l1.toArray())); 31 32 // 使用 for循環遍歷List 33 for(int i = 0 ; i < l1.size(); i++) { 34 if ("s2".equals(l1.get(1))) { 35 l1.remove(i); 36 i--; 37 } 38 } 39 System.out.println(Arrays.toString(l1.toArray())); 40 41 // 使用foreach遍歷List 42 for (String str : l1) { 43 if ("s3".equals(str)) { 44 l1.remove(str); // ArrayList內部方法remove(Object) 45 } 46 } 47 System.out.println(Arrays.toString(l1.toArray())); 48 } 49 50 }
能夠看到出現異常:ConcurrentModificationException,出現該異常緣由是:
「快速失敗」也就是fail-fast,它是Java集合的一種錯誤檢測機制。當建立Iterator後,在Iterator使用尚未結束時,改變(刪除或增添新項)集合元素就會出現上面的錯誤
看看ArrayList的排序方法:sort(Comparator<? super E> c)
1 public void sort(Comparator<? super E> c) { 2 final int expectedModCount = modCount; 3 Arrays.sort((E[]) elementData, 0, size, c); 4 if (modCount != expectedModCount) { 5 throw new ConcurrentModificationException(); 6 } 7 modCount++; 8 } 9 10 public static <T> void sort(T[] a, int fromIndex, int toIndex, 11 Comparator<? super T> c) { 12 if (c == null) { 13 sort(a, fromIndex, toIndex); 14 } else { 15 rangeCheck(a.length, fromIndex, toIndex); 16 if (LegacyMergeSort.userRequested) 17 legacyMergeSort(a, fromIndex, toIndex, c); 18 else 19 TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0); 20 } 21 }
示例:
1 package jdk.array; 2 3 import java.util.ArrayList; 4 import java.util.Arrays; 5 import java.util.Comparator; 6 import java.util.Iterator; 7 8 public class ArrayListTest2 { 9 10 public static void main(String[] args) { 11 12 ArrayList<String> l1 = new ArrayList<>(); 13 l1.add("s1"); 14 l1.add("s2"); 15 l1.add("s1"); 16 l1.add("s2"); 17 l1.add("s2"); 18 l1.add("s2"); 19 l1.add("s3"); 20 l1.add("s3"); 21 l1.add("s3"); 22 23 System.out.println(Arrays.toString(l1.toArray())); 24 25 l1.sort(null); 26 27 System.out.println(Arrays.toString(l1.toArray())); 28 29 l1.sort(new Comparator() { 30 31 @Override 32 public int compare(Object o1, Object o2) { 33 34 return -((String) o1).compareTo((String) o2); 35 } 36 37 }); 38 39 System.out.println(Arrays.toString(l1.toArray())); 40 41 } 42 43 }
(2)LinkedList
1 public class LinkedList<E> 2 extends AbstractSequentialList<E> 3 implements List<E>, Deque<E>, Cloneable, java.io.Serializable 4 // 實現了Deque接口,能夠作隊列使用 5 6 /** 7 * Pointer to first node. 8 * Invariant: (first == null && last == null) || 9 * (first.prev == null && first.item != null) 10 */ 11 transient Node<E> first; 12 13 /** 14 * Pointer to last node. 15 * Invariant: (first == null && last == null) || 16 * (last.next == null && last.item != null) 17 */ 18 transient Node<E> last; 19 20 /** 21 * Constructs an empty list. 22 */ 23 public LinkedList() { 24 } 25 26 // 集合對象存儲結構,經過當前節點的先後節點,維護順序集合(雙向鏈表結構) 27 private static class Node<E> { 28 E item; 29 Node<E> next; 30 Node<E> prev; 31 32 Node(Node<E> prev, E element, Node<E> next) { 33 this.item = element; 34 this.next = next; 35 this.prev = prev; 36 } 37 }
以上爲 LinkedList的內部存儲結構,以Node存儲。
在看下集合元素插入、刪除及獲取方法實現:
1 public boolean add(E e) { 2 linkLast(e); 3 return true; 4 } 5 6 /** 7 * Links e as last element. 8 */ 9 void linkLast(E e) { 10 final Node<E> l = last; // 保存最後個節點 11 final Node<E> newNode = new Node<>(l, e, null);// 新增節點 12 last = newNode; // 將新節點置爲最後節點 13 if (l == null) 14 first = newNode; 15 else 16 l.next = newNode; 17 size++; 18 modCount++; 19 } 20 21 public E remove() { 22 return removeFirst(); // 去掉首節點 23 } 24 25 public E removeFirst() { 26 final Node<E> f = first; 27 if (f == null) 28 throw new NoSuchElementException(); 29 return unlinkFirst(f); 30 } 31 32 private E unlinkFirst(Node<E> f) { 33 // assert f == first && f != null; 34 final E element = f.item; 35 final Node<E> next = f.next; 36 f.item = null; 37 f.next = null; // help GC 38 first = next; 39 if (next == null) 40 last = null; 41 else 42 next.prev = null; 43 size--; 44 modCount++; 45 return element; 46 } 47 48 // 入棧方法 49 public void push(E e) { 50 addFirst(e); 51 } 52 // 出棧方法 53 public E pop() { 54 return removeFirst(); 55 } 56 57 // 入隊 58 public boolean offer(E e) { 59 return add(e); 60 } 61 public boolean add(E e) { 62 linkLast(e); 63 return true; 64 } 65 // 出隊 66 public E poll() { 67 final Node<E> f = first; 68 return (f == null) ? null : unlinkFirst(f); 69 } 70 71 //隨機訪問集合對象 72 public E get(int index) { 73 checkElementIndex(index); 74 return node(index).item; 75 } 76 77 /** 78 * Returns the (non-null) Node at the specified element index. 79 */ 80 Node<E> node(int index) { 81 // assert isElementIndex(index); 82 // 識別 index id離首節點近仍是尾節點近,減小遍歷 83 if (index < (size >> 1)) { 84 Node<E> x = first; 85 for (int i = 0; i < index; i++) // 0(i) 86 x = x.next; 87 return x; 88 } else { 89 Node<E> x = last; 90 for (int i = size - 1; i > index; i--) // 0(i) 91 x = x.prev; 92 return x; 93 } 94 }
經過以上源碼理解 ArrayList 和 LinkedList 區別相似數據結構中 數組及鏈表結構區別 ,新增、刪除 和 隨機訪問存在 效率上的差異:
ArrayList是最經常使用的集合,其內部實現是一個數組,ArrayList的大小是能夠動態擴充的。對於元素的隨機訪問效率高,其訪問的時間複雜度爲O(1)
,對於數據的插入與刪除,從尾部操做效率高,時間複雜度和隨機訪問同樣是O(1)
,如果從頭部操做則效率會比較低,由於從頭部插入或刪除時須要移動後面全部元素,其時間複雜度爲O(n-i)
(n表示元素個數,i表示元素位置)
LinkList對於隨機訪問效率是比較低的,由於它須要從頭開始索引,因此其時間複雜度爲O(i)
。可是對於元素的增刪,LinkList效率高,由於只須要修改先後指針便可,其時間複雜度爲O(1)
。
(3)Vector
與ArrayList類型,內部也是使用數據來存儲對象,可是線程安全的,由於實現方法重寫的時候,所有加上了同步關鍵字:synchronized;(通常不建議使用,性能消耗)
(4)Stack
public class Stack<E> extends Vector<E> { /** * Creates an empty Stack. */ public Stack() { }
二、Queue
遵循FIFO(先入先出規則),內部出棧入棧方法,主要區別在因而否是阻塞入隊或出隊
三、Set
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable /** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * default initial capacity (16) and load factor (0.75). */ public HashSet() { map = new HashMap<>(); } // Set 集合對象存儲在 Map的 key中 public boolean contains(Object o) { return map.containsKey(o); } // 添加對象到Set集合中 public boolean add(E e) { return map.put(e, PRESENT)==null; } // 刪除Set集合中對象 public boolean remove(Object o) { return map.remove(o)==PRESENT; }
實際Set集合的實現依賴於Map的實現,經過Map的 key值惟一性來實現
2、Map
一、HashMap:基於Map接口實現、容許null 鍵值、無序、非同步
一塊兒看下HashMap的實現
// map 內部對象鏈表存儲結構 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 K getKey() { return key; } public final V getValue() { return value; } public final String toString() { return key + "=" + value; } // 重寫hashCode方法 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方法 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; // 用數組保存多條鏈表的首節點 // 獲取 key所對應的存儲JNode的 value值 public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } // 識別是否存在key所對應的 Node public boolean containsKey(Object key) { return getNode(hash(key), key) != null; }
// map 內部對象鏈表存儲結構 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 K getKey() { return key; } public final V getValue() { return value; } public final String toString() { return key + "=" + value; } // 重寫hashCode方法 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方法 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; // 用數組保存多條鏈表的首節點 // 獲取 key所對應的存儲JNode的 value值 public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } // 識別是否存在key所對應的 Node public boolean containsKey(Object key) { return getNode(hash(key), key) != 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) { // tab 爲map內首節點集合 Node<K,V>[] tab; Node<K,V> p; int n, i; // 先識別table是否爲空,爲空則初始化,hashmap內存存儲延遲加載在這裏體現 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // 經過hash值以長度作按位與,識別讀取元素的存儲在tab中的位置 if ((p = tab[i = (n - 1) & hash]) == null) // 若tab所在鏈表首節點爲空,則直接構造新節點 tab[i] = newNode(hash, key, value, null); else { // tab所在鏈表首節點不爲空,則遍歷p所在鏈表或紅黑樹,找到能夠存儲的位置 Node<K,V> e; K k; 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); 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; }
能夠看到,在HashMap中存儲的結構下, Node類型的數組保存頭部節點(單鏈表)或根節點(紅黑樹),先以 Node的key的hash值與數組長度作位與運算(hash碰撞),初始
時使用單鏈表存儲新插入對象(newNode),當鏈表長度超過8時,會將鏈表結構轉爲紅黑樹結構存儲(treeifyBin方法)
final void treeifyBin(Node<K,V>[] tab, int hash) { int n, index; Node<K,V> e; if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) resize(); else if ((e = tab[index = (n - 1) & hash]) != null) { // 找到須要轉換的 單鏈表 e,遍歷單鏈表,轉換爲TreeNode,保存先後節點關係 TreeNode<K,V> hd = null, tl = null; do { TreeNode<K,V> p = replacementTreeNode(e, null); if (tl == null) hd = p; else { p.prev = tl; tl.next = p; } tl = p; } while ((e = e.next) != null); //讓桶的第一個元素指向新建的紅黑樹頭結點,之後這個桶裏的元素就是紅黑樹而不是鏈表了 if ((tab[index] = hd) != null) hd.treeify(tab); } }
先將單鏈錶轉換爲 treenode,在調用 treeify方法構造紅黑樹
二、LinkedHashMap
繼承HashMap,HashMap是無序集合,而LinkedHashMap爲有序集合
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
構造LinkedHashMap.EntreyNode<K,V> 繼承 HashMap.Node<K,V> 實現雙向鏈表
static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; // before 保存前置節點,after保存後置節點 Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } } /** * The head (eldest) of the doubly linked list. */ transient LinkedHashMap.Entry<K,V> head; // 頭節點 /** * The tail (youngest) of the doubly linked list. */ transient LinkedHashMap.Entry<K,V> tail; // 尾部節點 // 重寫HashMap的 建立新節點方法 Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) { LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e); linkNodeLast(p); // 將新節點放到尾部節點,從而保證順序 return p; } // link at the end of list private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null) head = p; else { p.before = last; last.after = p; } }
三、TreeMap
TreeMap直接使用紅黑樹結構存儲集合元素,根據鍵 作排序,排序規則按內部 comparator 對象的實例對象的排序規則,若comparator爲空,則按天然排序
1 public class TreeMap<K,V> 2 extends AbstractMap<K,V> 3 implements NavigableMap<K,V>, Cloneable, java.io.Serializable 4 { 5 /** 6 * The comparator used to maintain order in this tree map, or 7 * null if it uses the natural ordering of its keys. 8 * 9 * @serial 10 */ 11 private final Comparator<? super K> comparator; // 對象比較接口 12 13 private transient Entry<K,V> root; // 根節點
root的實現邏輯爲 TreeMap.Entrey<K,V> 繼承 Map.Entrey<K,V> 實現 ,與HashMap.TreeNode<K,V>實現相似
static final class Entry<K,V> implements Map.Entry<K,V> { K key; V value; Entry<K,V> left; Entry<K,V> right; Entry<K,V> parent; boolean color = BLACK;
因此TreeMap put和get方法就是以鍵先進行紅黑樹的查找後操做
四、HashTable
HashTable和HashMap數據結構相似,主要區別爲HashTable中操做集合元素對象的方法都加上了 同步關鍵字(synchronized), 因此說線程安全的及集合