您的位置:首页 > 编程语言 > Java开发

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

2016-07-06 18:31 573 查看
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源码学习

学会看源码是一个程序员必备的技能!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息