一、前言
現筆者打算做關於Java集合框架的教程,具體是打算分析Java源碼,因為平時在寫程序的過程中用Java集合特別頻繁,但是對於里面一些具體的原理還沒有進行很好的梳理,所以擬從源碼的角度去熟悉梳理具體類的原理和其中的數據結構。分析源碼的好處總結如下三條:
1. 提升自身代碼水平及寫代碼能力。
2. 可以順帶溫習數據結構知識點。
3. 以后寫代碼遇到問題時能夠找到最佳的解決辦法
二、集合框架圖
做一件事情時,首先一定要有做事情的總體方法,然后再去摳細節。我們肯定要來看看集合的總體框架圖,也好對集合框架有一個很感性的認識。下圖展示了Java整個集合框架(沒有包括並發),如果不出意外的話,以后也會出並發方面的專題分析,我們從表及里,由淺入深,慢慢來。
說明:對於以上的框架圖有如下幾點說明
1. 集合接口:6個接口(短虛線表示),表示不同集合類型,是集合框架的基礎。
2. 抽象類:5個抽象類(長虛線表示),對集合接口的部分實現。可擴展為自定義集合類。
3. 實現類:8個實現類(實線表示),對接口的具體實現。
4. Collection 接口是一組允許重復的對象。
5. Set 接口繼承 Collection,集合元素不重復。
6. List 接口繼承 Collection,允許重復,維護元素插入順序。
7. Map接口是鍵-值對象,與Collection接口沒有什么關系。
三、接口說明
3.1. Collection接口
除了Map接口,其他集合都是Collection的子類,並且在我們的實際編程中,由於多態的原因,我們一般都會使用這個的編碼方式,如:Inter i1 = new ImplementInter();(其中,Inter表示一個接口,ImplementInter表示對此接口的實現),此時i1調用的方法只能是Inter接口中的方法,無法調用ImplementInter中新增的方法(除非進行向下類型轉化)。所以,很有必要了解一下Collection根接口中都有哪些方法。

public interface Collection<E> extends Iterable<E> { int size(); boolean isEmpty(); boolean contains(Object o); Iterator<E> iterator(); Object[] toArray(); <T> T[] toArray(T[] a); boolean add(E e); boolean remove(Object o); boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); boolean removeAll(Collection<?> c); boolean retainAll(Collection<?> c); void clear(); boolean equals(Object o); int hashCode(); // jdk1.8添加的方法 default boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); boolean removed = false; final Iterator<E> each = iterator(); while (each.hasNext()) { if (filter.test(each.next())) { each.remove(); removed = true; } } return removed; } @Override default Spliterator<E> spliterator() { return Spliterators.spliterator(this, 0); } default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } default Stream<E> parallelStream() { return StreamSupport.stream(spliterator(), true); } }
說明:
1. 其中在jdk1.8后添加的方法對我們的分析不會產生影響,添加的方法有關鍵字default修飾,為缺省方法,是一個新特性。
2. 對集合而言,都會包含添加、刪除、判斷、清空、大小等基本操作。
3.2. Map接口
對於Map接口而言,是鍵值對集合,特別適用於那種情形,一個主屬性,另外一個副屬性(如:姓名,性別;leesf,男),添加元素時,若存在相同的鍵,則會用新值代替舊值。方法如下

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 putAll(Map<? extends K, ? extends V> m); void clear(); Set<K> keySet(); Collection<V> values(); Set<Map.Entry<K, V>> entrySet(); interface Entry<K,V> { K getKey(); V getValue(); V setValue(V value); boolean equals(Object o); int hashCode(); // jdk1.8 后添加的方法 public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() { return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> c1.getKey().compareTo(c2.getKey()); } public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() { return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> c1.getValue().compareTo(c2.getValue()); } public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) { Objects.requireNonNull(cmp); return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey()); } public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) { Objects.requireNonNull(cmp); return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue()); } } boolean equals(Object o); int hashCode(); default V getOrDefault(Object key, V defaultValue) { V v; return (((v = get(key)) != null) || containsKey(key))? v: defaultValue; } default void forEach(BiConsumer<? super K, ? super V> action) { Objects.requireNonNull(action); for (Map.Entry<K, V> entry : entrySet()) { K k; V v; try { k = entry.getKey(); v = entry.getValue(); } catch(IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } action.accept(k, v); } } default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { Objects.requireNonNull(function); for (Map.Entry<K, V> entry : entrySet()) { K k; V v; try { k = entry.getKey(); v = entry.getValue(); } catch(IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } // ise thrown from function is not a cme. v = function.apply(k, v); try { entry.setValue(v); } catch(IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } } } default V putIfAbsent(K key, V value) { V v = get(key); if (v == null) { v = put(key, value); } return v; } default boolean remove(Object key, Object value) { Object curValue = get(key); if (!Objects.equals(curValue, value) || (curValue == null && !containsKey(key))) { return false; } remove(key); return true; } default boolean replace(K key, V oldValue, V newValue) { Object curValue = get(key); if (!Objects.equals(curValue, oldValue) || (curValue == null && !containsKey(key))) { return false; } put(key, newValue); return true; } default V replace(K key, V value) { V curValue; if (((curValue = get(key)) != null) || containsKey(key)) { curValue = put(key, value); } return curValue; } 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; } default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); V oldValue; if ((oldValue = get(key)) != null) { V newValue = remappingFunction.apply(key, oldValue); if (newValue != null) { put(key, newValue); return newValue; } else { remove(key); return null; } } else { return null; } } default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); V oldValue = get(key); V newValue = remappingFunction.apply(key, oldValue); if (newValue == null) { // delete mapping if (oldValue != null || containsKey(key)) { // something to remove remove(key); return null; } else { // nothing to do. Leave things as they were. return null; } } else { // add or replace old mapping put(key, newValue); return newValue; } } default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); Objects.requireNonNull(value); V oldValue = get(key); V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value); if(newValue == null) { remove(key); } else { put(key, newValue); } return newValue; } }
說明:
1. Map接口有一個內部接口Entry,對集合中的元素定義了一組通用的操作,維護這鍵值對,可以對鍵值對進行相應的操作,通過Map接口的entrySet可以返回集合對象的視圖集,方便對集合對象進行遍歷等操作。
2. 對Map而言,也會包含添加、刪除、判斷、清空、大小等基本操作。
3.3. Comparable接口 && Comparator接口
此接口的作用是對集合中的元素進行排序,如Integer類型默認實現了Comparable<Integer>,String類型默認實現了Comprable<String>接口,Integer與String實現了這個接口有什么作用呢?就是當集合中的元素類型為Integer或者是String類型時,我們可以直接進行排序,就可以返回自然排序后的集合。
對於Comparable接口而言,只有一個方法。

public interface Comparable<T> { public int compareTo(T o); }
我們在compareTo方法中實現我們的邏輯,就可以實現各種各樣的排序。
對於Comparator接口而言,比Comparable接口類似,用作排序元素,主要的方法如下

public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); // jdk1.8 后的方法 default Comparator<T> reversed() { return Collections.reverseOrder(this); } default Comparator<T> thenComparing(Comparator<? super T> other) { Objects.requireNonNull(other); return (Comparator<T> & Serializable) (c1, c2) -> { int res = compare(c1, c2); return (res != 0) ? res : other.compare(c1, c2); }; } default <U> Comparator<T> thenComparing( Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator) { return thenComparing(comparing(keyExtractor, keyComparator)); } default <U extends Comparable<? super U>> Comparator<T> thenComparing( Function<? super T, ? extends U> keyExtractor) { return thenComparing(comparing(keyExtractor)); } default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) { return thenComparing(comparingInt(keyExtractor)); } default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) { return thenComparing(comparingLong(keyExtractor)); } default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) { return thenComparing(comparingDouble(keyExtractor)); } public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() { return Collections.reverseOrder(); } @SuppressWarnings("unchecked") public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() { return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE; } public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) { return new Comparators.NullComparator<>(true, comparator); } public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) { return new Comparators.NullComparator<>(false, comparator); } public static <T, U> Comparator<T> comparing( Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator) { Objects.requireNonNull(keyExtractor); Objects.requireNonNull(keyComparator); return (Comparator<T> & Serializable) (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1), keyExtractor.apply(c2)); } public static <T, U extends Comparable<? super U>> Comparator<T> comparing( Function<? super T, ? extends U> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T> & Serializable) (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2)); } public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T> & Serializable) (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2)); } public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T> & Serializable) (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2)); } public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T> & Serializable) (c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2)); } }
我們在compare方法實現我們的比較邏輯,就可以實現各種各樣的排序。
四、工具類Collections && Arrays
Collections與Arrays工具類提供了很多操作集合的方法,具體的我們可以去查看API,總有一款你想要的。
五、equals && hashCode
equals方法與hashCode方法在集合中顯得尤為重要,所以,在這里我們也好好的理解一下,為后邊的分析打下好的基礎。 在每一個覆蓋了equals方法的類中,也必須覆蓋hashCode方法,因為這樣會才能使得基於散列的集合正常運作。
Object規范規定:
1. 在應用程序的執行期間,只要對象的equals方法的比較操作所用到的信息沒有被修改,那么對這同一個對象調用多次hashCode方法都必須始終如一的返回同一個整數。在同一個應用程序的多次執行過程中,每次執行所返回的整數可以不一致。
2. 如果兩個對象根據equals方法比較是相等的,那么調用者兩個對象中的任意一個對象的hashCode方法都必須產生同樣的整數結果。
3. 如果兩個對象根據equals方法比較是不相等的,那么調用這兩個對象中任意一個對象的hashCode方法,則不一定產生不同的整數結果。
相等的對象必須擁有相等的散列碼。即equals相等,則hashcode相等,equals不相等,則hashcode不一定相等。一個好的hashCode函數傾向於為不相等的對象產生不相等的散列碼,從而提升性能,不好的hashCode函數會讓散列表退化成鏈表,性能急劇下降。
六、總結
集合的開篇之作就到這里了,之后會不定期的進行更新,盡請期待,謝謝各位園友觀看~