【集合框架】Java集合框架綜述

leesf發表於2016-03-04

一、前言

  現筆者打算做關於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);
    }
}
View Code

說明:

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

說明:

  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);
}
View Code

  我們在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));
    }
}
View Code

  我們在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函式會讓雜湊表退化成連結串列,效能急劇下降。

六、總結

  集合的開篇之作就到這裡了,之後會不定期的進行更新,盡請期待,謝謝各位園友觀看~

 

  

 

  

相關文章