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


一、前言

  現筆者打算做關於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函數會讓散列表退化成鏈表,性能急劇下降。

六、總結

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

 

  

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM