从源码角度解析Java集合框架

来源:互联网 发布:linux cp复制文件夹 编辑:程序博客网 时间:2024/05/29 15:48

1.ArrayList(允许重复的元素,允许null值,非线程同步的(Vector是同步的),其内部实现还是数组)

    public boolean contains(Object o) {        return indexOf(o) >= 0;    }    public int indexOf(Object o) {        if (o == null) {            for (int i = 0; i < size; i++)                if (elementData[i]==null)                    return i;        } else {            for (int i = 0; i < size; i++)                if (o.equals(elementData[i]))                    return i;        }        return -1;    }

从源码中可以看出,contains方法是遍历object数组,通过object的equals方法来比较的

    /**     * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The     * elements themselves are not copied.)     *     * @return a clone of this <tt>ArrayList</tt> instance     */    public Object clone() {        try {            @SuppressWarnings("unchecked")                ArrayList<E> v = (ArrayList<E>) super.clone();            v.elementData = Arrays.copyOf(elementData, size);            v.modCount = 0;            return v;        } catch (CloneNotSupportedException e) {            // this shouldn't happen, since we are Cloneable            throw new InternalError();        }    }

这回我们不用看代码,直接看代码上面的注释就可以知道ArrayList的clone方法是浅拷贝。(当然,对应的还有深拷贝,什么是浅拷贝,深拷贝,大家去百度吧,这里不作解释)

add方法不看源码,因为涉及到的方法很多,结论是,每次调用add方法的时候,系统会监测是否超过ArrayList的最大承受范围,如果超过了,就将ArrayList的容量扩大一倍,然后再进行元素的添加

remove方法

public E remove(int index) {        rangeCheck(index);        modCount++;        E oldValue = elementData(index);        int numMoved = size - index - 1;        if (numMoved > 0)            System.arraycopy(elementData, index+1, elementData, index,                             numMoved);        elementData[--size] = null; // clear to let GC do its work        return oldValue;    }

先检查要remove元素的下标是否越界,如果没有越界,就将该元素后面的所有元素往前移一位,最后将需要删除的那个元素赋值null,目的是通知垃圾回收机制去回收该对象

ArrayList中的Iterator,这相当于一个遍历器,能够依次访问到每个元素

ArrayList中的一些常见的方法都分析完了,其他的如果想自己分析,就查看JDK源码吧。与arraylist类似的还有LinkedList,内部是一个双向链表,学过数据结构的同学都知道,链表的插入元素和删除元素的时候比较方便,所以当数据量非常大的时候,用LinkedList更高效。

2.HashSet
开发中HashSet用的比较多,它不允许重复的元素,允许null值,而且是无序的,它内部是基于HashMap实现的。它有两个关键的因数,一个是capacity,另一个是load factor。官方建议不要将capacity设置太大或者将load factor设置太小。如果capacity设置太大,bucket就放不满,占用过多无效的内存空间;如果load factor设置过小,系统就会频繁得给bucket扩容,影响程序的运行效率。

    /**     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has     * default initial capacity (16) and load factor (0.75).     */    public HashSet() {        map = new HashMap<>();    }

从源码中可以看出,默认的capacity是16,load factor是0.75。具体意思就是当里面的元素个数达到16*0.75=12时,系统就会为HashSet扩容,扩容到当前的两倍

    public int size() {        return map.size();    }    public boolean contains(Object o) {        return map.containsKey(o);    }    public boolean add(E e) {        return map.put(e, PRESENT)==null;    }    public boolean remove(Object o) {        return map.remove(o)==PRESENT;    }    public void clear() {        map.clear();    }

这几个方法内部都是通过HashMap来实现的,其他方法不赘述,其实都差不多
由于HashSet是无序的,所以没有办法通过正常的循环结构来遍历其中的数据,JDK给我们提供了Iterator接口,使用方法和ArrayList中的Iterator方法一样
与HashSet类似的还有LinkedHashSet,看名字就能猜得出来,内部实现原理是链表;TreeMap,可以用来排序的Set,初始化时候需要传一个Comparator参数

3.HashMap
HashMap允许空值,而Hashtable不允许空值
这里我们重点看put和get方法

    public V put(K key, V value) {        //如果HashMap内部的table是空的,就实例化一个table        if (table == EMPTY_TABLE) {            inflateTable(threshold);        }        //如果key为null,就调用putForNullKey()        if (key == null)            return putForNullKey(value);        //如果key不为null,就找到key的hashcode所对应的地址,在该地址里面遍历所有的entry,看是否有相同的元素,相同则替换旧的value,不同就添加该元素        int hash = hash(key);        int i = indexFor(hash, table.length);        for (Entry<K,V> e = table[i]; e != null; e = e.next) {            Object k;            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {                V oldValue = e.value;                e.value = value;                e.recordAccess(this);                return oldValue;            }        }        modCount++;        addEntry(hash, key, value, i);        return null;    }

get方法原理跟这个差不多,就不赘述了

HashMap中值得注意的一个知识点是,要判断两个元素是否相等,必须要同时满足hashcode和equals相同,只要有其中一个值不相同,就视为不同元素

与HashMap类似的一些集合类使用方法跟它差不多,所有就不多说了,可以自己查看JDK源码学习

学会看源码是一个程序员必备的技能!

1 0