一、前言
現筆者打算做關於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函式會讓雜湊表退化成連結串列,效能急劇下降。
六、總結
集合的開篇之作就到這裡了,之後會不定期的進行更新,盡請期待,謝謝各位園友觀看~