JDK源码剖析之集合


点击查看JDK源码剖析系列


目录

前言

Java集合分为CollectionMap两种,在实际开发中使用频率极高,其实现细节和用法在面试中几乎是必问的知识点。在特定的需求环境下选用合适的子类可以提高代码质量,并使业务实现更加轻松。

Java集合体系结构

  • Java中的集合主要分为CollectionMap两类,并且所有集合都继承或依赖于Collection
  • Collection分为ListSetQueue
  • List分为LinkedListArrayListVector,Vector有子类Stack
  • Set分为HashSetTreeSet
  • Map分为TreeMapHashMapWeakHashMapHashtable

体系结构图如下:
Collection集合体系结构图
其中

  • Set 放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中,非线程安全
  • Map 存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。可以接受空字串 “”,或者 null 作为键值,非线程安全
  • ArrayList 有序,可以包含重复的元素,通过索引访问。查询效率高,增删元素效率低,非线程安全
  • LinkedList 有序,可以包含重复的元素,通过索引访问。查询效率低,增删元素效率高,非线程安全
  • Vector 基本类似ArrayList,但是线程安全
  • Stack 继承自Vector,实现一个后进先出的栈,线程安全
  • Hashtable 继承Map接口,实现一个key-value映射的哈希表。键值对不接受null。get与put操作时间开销为常数,线程安全
  • HashMapHashtable类似,键值对可接受null,但是将HashMap视为Collection时,其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。非线程安全
  • WeakHashMap 一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。非线程安全
  • TreeMap 基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,基本操作containsKey、get、put和remove的时间复杂度是 log(n) 。非线程安全

集合源代码剖析

一、Collection接口

public interface Collection<E> extends Iterable<E> {
    int size();//返回集合中元素的个数
    boolean isEmpty();//如果集合中没有元素,返回true
    boolean contains(Object o);//如果集合中包含特定对象,返回true
    Iterator<E> iterator();//返回该集合对象的迭代器
    Object[] toArray();//返回包含该集合对象所有元素的Object类型数组
    <T> T[] toArray(T[] a);// 如果集合与指定数组长度和类型一致,则将集合的元素放在数组里返回。否则,将分配一个新数组,数组的大小和这个集合的大小相同。如果指定数组长度小于集合的长度,则将集合的元素放在数组里后,用null填充后再返回该数组。
    boolean add(E e);//添加元素,成功返回true
    boolean remove(Object o);//删除元素,成功返回true
    boolean containsAll(Collection<?> c);//如果集合包含传入集合的所有元素,返回true
    boolean addAll(Collection<? extends E> c);//将传入集合的所有元素追加至该集合的末尾
    boolean removeAll(Collection<?> c);//将集合中与传入集合中所有相同的元素移除
    /**
     * 1.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;
    }
    boolean retainAll(Collection<?> c);//如果存在相同元素,这个集合中仅保留相同的元素并返回true。如果不存在相同元素,这个会变为空并返回true。P.S.如果集合相同,则返回false且什么都不做
    void clear();//清空集合
    boolean equals(Object o);//判断两集合是否相等
    int hashCode();//计算并返回集合的hash值
    @Override
    default Spliterator<E> spliterator() {//JDK1.8新加入的模板方法,获得可并行处理的迭代器
        return Spliterators.spliterator(this, 0);
    }
    default Stream<E> stream() {//JDK1.8新加入的模板方法,获得集合的stream对象,对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作。像一个高级版本的Iterator。
        return StreamSupport.stream(spliterator(), false);
    }
    default Stream<E> parallelStream() {//JDK1.8新加入的模板方法,获得并行执行的流.它通过默认的ForkJoinPool,可能提高你的多线程任务的速度。
        return StreamSupport.stream(spliterator(), true);
    }
}

Collection接口定义了一组有关集合的常规操作,不做赘述。
值得关注的是JDK1.8之后新加入的几个模板方法:

  • removeIf
  • retainAll
  • spliterator
  • stream
  • parallelStream

二、List

public interface List<E> extends Collection<E> {
    ...
    //省略Collection中出现过的函数
    ...
    boolean addAll(int index, Collection<? extends E> c);//将集合c中的所有元素插入到此集合中,插入位置位index
    E get(int index);//按下标获得集合中元素
    E set(int index, E element);//按下标设置集合中元素
    void add(int index, E element);//按下标向集合添加一个元素
    E remove(int index);//按下标移除一个元素
    int indexOf(Object o);//获得某对象的下标值,若集合有多个该元素,则返回第一个相同元素的下标
    int lastIndexOf(Object o);//获得某对象在集合中最后一次出现的下标值
    ListIterator<E> listIterator();//获得list迭代器,和普通迭代器相比多了add,set,remove,向前迭代等操作
    ListIterator<E> listIterator(int index);//获得list迭代器,保留下标为index以及之后的元素
    List<E> subList(int fromIndex, int toIndex);//返回原来list的从fromIndex到toIndex(左闭右开)之间这一部分的视图,慎用
    //JDK1.8加入的模板方法,根据UnaryOperator的apply方法返回的结果,替换集合中所有的元素
    default void replaceAll(UnaryOperator<E> operator) {
        Objects.requireNonNull(operator);
        final ListIterator<E> li = this.listIterator();
        while (li.hasNext()) {
            li.set(operator.apply(li.next()));
        }
    }
    //JDK1.8加入的模板方法,通过实现compare方法,按一定规则对集合中的对象进行排序
    @SuppressWarnings({"unchecked", "rawtypes"})
    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }
}

在Collection的基础上加入了get、set、indexOf、lastIndexOf等针对元素操作的方法,还增加了针对整个集合进行操作的方法如listIterator、subList、replaceAll、sort等。对于JDK1.8新加入的几个模板方法我们可以特别留意一下:

  • replaceAll
  • sort

三、Set接口

public interface Set<E> extends Collection<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 retainAll(Collection<?> c);
    boolean removeAll(Collection<?> c);
    void clear();
    boolean equals(Object o);
    int hashCode();
    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.DISTINCT);
    }
}

Set中的方法在Collection中都有,不做赘述。

四、Queue

public interface Queue<E> extends Collection<E> {
    boolean add(E e);//向队列末尾添加一个元素,超出队列界限会抛异常
    boolean offer(E e);//向队列末尾添加一个元素,超出队列界限不会抛异常(返回false)
    E remove();//移除并返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常
    E poll();//移除并返问队列头部的元素,如果队列为空,则返回null
    E element();//返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常
    E peek();//返回队列头部的元素,如果队列为空,则返回null
}

Queue队列在数据结构中是一种特殊的线性表,元素只能添加到末尾,只能获取第一个元素,即先进先出(FIFO)。

五、Map

public interface Map<K,V> {
    int size();//返回Map的元素个数
    boolean isEmpty();//元素个数是否为0
    boolean containsKey(Object key);//是否拥有某个key
    boolean containsValue(Object value);//是否拥有某个值
    V get(Object key);//根据key获得value
    V put(K key, V value);//放入一个元素
    V remove(Object key);//根据key移除value
    void putAll(Map<? extends K, ? extends V> m);//可以合并两个MAP,如果有相同的key那么用后面的覆盖前面的
    void clear();//清空map
    Set<K> keySet();//返回Map中所有key的set对象
    Collection<V> values();//返回map中所有value的Collection
    Set<Map.Entry<K, V>> entrySet();//返回一个表示映射项(里面有Key和Value)的Set
    /**
      *Map.Entry 是Map中的一个接口,他的用途是表示一个映射项(里面有Key和Value),而              
      *Set<Map.Entry<K,V>>表示一个映射项的Set。Map.Entry里有相应的getKey和getValue方法,
      *即JavaBean,让我们能够从一个项中取出Key和Value。
      */
    interface Entry<K,V> {
        K getKey();//返回这个entry的key
        V getValue(); //返回这个entry的value
        V setValue(V value);//设置该entry的value
        boolean equals(Object o);//如果给出的Object也是entry并且代表相同的映射,则返回true
        int hashCode();//返回Hash码
        //返回关于key的Comparator对象
        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());
        }
        //返回关于value的Comparator对象
        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());
        }
        //用已有的Comparator返回关于key的Comparator对象
        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());
        }
        //用已有的Comparator返回关于value的Comparator对象
        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);//比较两个map是否相同
    int hashCode();//返回map的Hash码
    //根据key获取value,若不存在返回默认值
    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }
    //JDK1.8新加入模板方法,函数式编程,可以在遍历的同时进行操作
    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);
        }
    }
    //JDK1.8新加入,对每个元素执行operator指定的操作,并用操作结果来替换原来的元素。其中UnaryOperator是一个函数接口,里面只有一个待实现函数T apply(T t)。
    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);
            }
        }
    }
    //尝试添加一个元素,若该key已被占用,则不添加
    default V putIfAbsent(K key, V value) {
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }

        return v;
    }
    //如果key对应的value和传入的value相等,则移除该key对应的元素
    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;
    }
    //替换元素,如果oldvalue和key对应的value不一致,则不替换并返回false
    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;
    }
    //如果存在key,则将key对应的value替换为新值
    default V replace(K key, V value) {
        V curValue;
        if (((curValue = get(key)) != null) || containsKey(key)) {
            curValue = put(key, value);
        }
        return curValue;
    }
    /**
      * 当前Map中不存在key值的映射或映射值为null时,才调用mappingFunction,
      * 并在mappingFunction执行结果非null时,将结果跟key关联,
      * Function是一个函数接口,里面有一个待实现方法R apply(T t)。
      * computeIfAbsent()常用来对Map的某个key值建立初始化映射.
      * 比如我们要实现一个多值映射,Map的定义可能是Map<K,Set<V>>,要向Map中放入新值
      */
    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;
    /**
      * 作用跟computeIfAbsent()相反,即,只有在当前Map中存在key值的映射且非null时,
      * 才调用remappingFunction,如果remappingFunction执行结果为null,则删除key的映射,
      * 否则使用该结果替换key原来的映射.
      */
    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;
        }
    }
    /**
      * 作用是把remappingFunction的计算结果关联到key上,
      * 如果计算结果为null,则在Map中删除key的映射.
      */
    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) {
            if (oldValue != null || containsKey(key)) {
                remove(key);
                return null;
            } else {
                return null;
            }
        } else {
            put(key, newValue);
            return newValue;
        }
    /**
      * 如果Map中key对应的映射不存在或者为null,则将value(不能是null)关联到key上;
      * 否则执行remappingFunction,如果执行结果非null则用该结果跟key关联,
      * 否则在Map中删除key的映射.
      */
    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;
    }
}

对于JDK1.8新加入的几个模板方法我们可以特别留意一下:

  • comparingByKey
  • comparingByValue
  • forEach
  • replaceAll
  • putIfAbsent
  • computeIfAbsent
  • computeIfPresent
  • compute
  • merge

猜你喜欢

转载自blog.csdn.net/qq_33829547/article/details/80266671