深入理解java集合框架(jdk1.6源码)
原文链接: http://www.imrookie.cn/article/java-collection-jdk1u6
简化体系图
1.最顶层的Collection接口,
里面定义了一些抽象方法,源码如下:
package java.util;publicinterfaceCollection<E>extendsIterable<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); //判断是否包含指定collection中的所有元素boolean addAll(Collection<?extends E> c); //插入指定collection中的所有元素boolean removeAll(Collection<?> c); //移除此collection中那些也包含在指定collectiion中的元素boolean retainAll(Collection<?> c); //仅保留此collection中那些也包含在指定collection中的元素void clear(); //移除所有元素boolean equals(Object o); //比较此collection与指定对象是否相等int hashCode(); //返回此collection的hash值}
Collection接口继承了Iterable接口,内部就一个抽象方法,用来返回迭代器,源码如下:
package java.lang;import java.util.Iterator;publicinterfaceIterable<T>{Iterator<T> iterator(); //返回迭代器}
迭代器接口IteraTor源码如下:
package java.util;publicinterfaceIterator<E>{boolean hasNext(); //是否含有下一个元素E next(); //返回迭代的下一个元素void remove(); //从迭代器指向的collection中移除迭代器返回的最后一个元素}
·抽象类AbstractCollection,实现了Collection接口
在AbstractCollection抽象类里,实现了下列方法:
在java的API文档里面标明可选操作的方法,在子类中如果需要则需要重写该方法,否则会报new UnsupportedOperationException();
1. boolean isEmpty()方法,源码如下,这里并没有提供size()的实现,所以需要在子类中实现size()方法
publicboolean isEmpty(){return size()==0;}
2. boolean contains(Object o),在该方法中,允许传入null值,如果元素中有null,则返回true
实现原理:取迭代器,如果传入null,则循环判断是否有null元素,有则true;如果传入不为null,则遍历迭代器,利用equals()方法进行判断是否相等;同样的iterator()方法需要在子类中实现.
publicboolean contains(Object o){Iterator<E> e = iterator();if(o==null){while(e.hasNext())if(e.next()==null)returntrue;}else{while(e.hasNext())if(o.equals(e.next()))returntrue;}returnfalse;}
3. Object[] toArray()和<T> T[] toArray(T[] a)方法;
4. boolean remove(Object o) 移除某元素,实现类似于contains(Object o)方法,先找到该元素,然后调用迭代器的remove()方法,然后返回true,如果没有该元素,返回false;
5. boolean containsAll(Collection<?> c) 判断全部包含,处理过程:取形参c的迭代器,遍历该形参迭代器,循环调用contains(Object o)方法判断,有一个不包含则返回false;
6. boolean addAll(Collection<? extends E> c) ,从源码看,只要有一个插入成功就返回true
publicboolean addAll(Collection<?extends E> c){boolean modified =false;Iterator<?extends E> e = c.iterator();while(e.hasNext()){if(add(e.next()))modified =true;}return modified;}
7. boolean removeAll(Collection<?> c) 和addAll类似,有一个移除了就返回true;
8. boolean retainAll(Collection<?> c) 和removeAll类似,有一个删除就返回true;
9. void clear() 处理过程:循环调用迭代器的remove();
10. String toString() 重写了toString()方法
2.Collection接口下的3个子接口,List,Set,Queue
2.1 List接口
List接口继承了Collection接口,源码如下(继承自Collection接口的方法此处省略):
package java.util;publicinterfaceList<E>extendsCollection<E>{//...继承自Collection的抽象方法省略E get(int index);//返回列表中指定位置的元素E set(int index, E element);//用指定元素替换列表中指定位置的元素void add(int index, E element);//在列表的指定位置插入元素E remove(int index);//删除指定位置上的元素int indexOf(Object o);//返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1int lastIndexOf(Object o);//返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1ListIterator<E> listIterator();//返回此列表元素的列表迭代器(按适当顺序)ListIterator<E> listIterator(int index);//返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始List<E> subList(int fromIndex,int toIndex);//返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的子列表}
可以看到,listIterator()方法返回了list列表迭代器,来看下源码:
ListIterator接口:
ListIterator列表迭代器接口,继承了Iterator接口:
package java.util;publicinterfaceListIterator<E>extendsIterator<E>{//继承自Iterator接口的方法就不显示了boolean hasNext();E next();boolean hasPrevious(); //如果以逆向遍历列表,列表迭代器有多个元素,则返回 true,也就是判断是否存在上一个元素E previous(); //返回上一个元素int nextIndex(); //下一个元素的索引int previousIndex(); //上一个元素的索引void remove(); //从列表中移除由 next 或 previous 返回的最后一个元素void set(E e); //用指定元素替换next或previous返回的最后一个元素void add(E e); //将指定的元素插入列表}
抽象类AbstractList
AbstractList继承自AbstractCollection,实现了List接口
在AbstractList抽象类中,实现了iterator()方法和listIterator()方法,因为很多方法基于这两个方法来实现,比如AbstractList里的indexOf和lastIndexOf方法等;
1.内部类Itr,实现了Iterator接口,一起看下源码(分析写在注释部分):
privateclassItrimplementsIterator<E>{int cursor =0; //当前游标位置- int lastRet =-1; //上一个操作的元素索引
int expectedModCount = modCount; //预期修改数,初始化为外部类的修改数的值,用来作并发判断- publicboolean hasNext(){
return cursor != size();}- //可以看出,iterator中并没有元素容器,他是靠cursor属性和lastRet属性来控制读取和删除操作的
- public E next(){
checkForComodification();try{E next = get(cursor);lastRet = cursor++;return next;}catch(IndexOutOfBoundsException e){checkForComodification();thrownewNoSuchElementException();}}//注意: 不可以连续使用该方法,否则会因为lastRet属性被重置为-1而报异常- publicvoid remove(){
if(lastRet ==-1)thrownewIllegalStateException();checkForComodification();try{AbstractList.this.remove(lastRet);if(lastRet < cursor)cursor--;lastRet =-1;expectedModCount = modCount;}catch(IndexOutOfBoundsException e){thrownewConcurrentModificationException();}}- //此用法用来判断外部修改数和iterator修改数是否一致,不一致会报异常
- //所以,在使用iterator期间不可以用非iterator方法对list做操作
- finalvoid checkForComodification(){
if(modCount != expectedModCount)thrownewConcurrentModificationException();}- }
注意: 在list中使用iterator时千万不要使用非iterator方法对list进行改动,否则会报异常:java.util.ConcurrentModificationException
2.内部类ListItr,继承了Itr,实现了ListIterator接口;
在Itr的基础上,多了一些方法,支持向前读,插入,set等操作;
提供了一个带参构造函数,设置cursor参数的值.
privateclassListItrextendsItrimplementsListIterator<E>{ListItr(int index){cursor = index;}publicboolean hasPrevious(){return cursor !=0;}public E previous(){checkForComodification();try{int i = cursor -1;E previous = get(i);lastRet = cursor = i;return previous;}catch(IndexOutOfBoundsException e){checkForComodification();thrownewNoSuchElementException();}}publicint nextIndex(){return cursor;}publicint previousIndex(){return cursor-1;}publicvoid set(E e){if(lastRet ==-1)thrownewIllegalStateException();checkForComodification();try{AbstractList.this.set(lastRet, e);expectedModCount = modCount;}catch(IndexOutOfBoundsException ex){thrownewConcurrentModificationException();}}publicvoid add(E e){checkForComodification();try{AbstractList.this.add(cursor++, e); //在当前游标索引处插入元素,然后光标自增1lastRet =-1;expectedModCount = modCount;}catch(IndexOutOfBoundsException ex){thrownewConcurrentModificationException();}}}
2.1.1 ArrayList类
ArrayList类继承自AbstractList类,实现了List,RandomAccess,Cloneable,java.io.Serializable接口,是可序列化的.
在ArrayList中使用对象数组来作为存储容器,使用transient关键字修饰,transient关键字具有以下特征:
1、transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。
2、被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
3、一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。也可以认为在将持久化的对象反序列化后,被transient修饰的变量将按照普通类成员变量一样被初始化
ArrayList类提供了3个构造方法:
1.带参构造方法,参数为容器初始化大小
2.无参构造方法,默认容器大小为10
3.带参构造方法,参数为Collection实例
ArrayList类中的重要属性包括:
privatetransientObject[] elementData; //容器privateint size; //记录元素个数protected transient int modCount = 0; //结构上改动的次数,例如扩容操作,继承自AbstractList父类
ArrayList类中的常用方法:
1. 扩容 publicvoid ensureCapacity(int minCapacity)
从源码中可以看出,当传入参数大于当前容量时有效,该方法扩容至少为 原容量的1.5倍+1并取整
publicvoid ensureCapacity(int minCapacity){modCount++;int oldCapacity = elementData.length;if(minCapacity > oldCapacity){Object oldData[]= elementData;int newCapacity =(oldCapacity *3)/2+1;if(newCapacity < minCapacity)newCapacity = minCapacity;// minCapacity is usually close to size, so this is a win:elementData =Arrays.copyOf(elementData, newCapacity);}}
2. 将此 ArrayList 实例的容量调整为列表的当前大小 public void trimToSize()
也就是把容器的大小调整为size属性大小
3. indexOf(Object o) 返回某对象在数组容器中第一次出现的索引,没有该元素则返回-1
参数可以是null
4. lastIndexOf(Object o) 返回某对象在数组容器中最后一次出现的索引,没有该元素则返回-1
参数可以是null
5. 克隆方法Object clone()
因为超类中的clone()方法是浅拷贝的,所以要实现深拷贝,要在super.clone()之后,将引用变量重新赋值;
publicObject clone(){try{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 CloneablethrownewInternalError();}}
6. 转为数组
Object[] toArray() 和
public <T> T[] toArray(T[] a)7. 取值 get(int index)
8. 设置某索引对应的元素 set(int index, E element)
该方法返回该索引上的旧值
在末尾添加元素 boolean add(E e)
9. 在指定索引位置插入元素 add(int index, E element)
原理: 数组容器从该索引位置整体向后挪动1位,然后给改索引位置赋值
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 复制指定的数组到目标数组
参数1: 要复制的原始数组
参数2: 从哪开始拷贝
参数3: 目的地数组
参数4: 开始存放的位置,也就是复制的元素从哪个索引开始放
参数5: 拷贝长度
publicvoid add(int index, E element){if(index > size || index <0)thrownewIndexOutOfBoundsException("Index: "+index+", Size: "+size);ensureCapacity(size+1);// Increments modCount!!System.arraycopy(elementData, index, elementData, index +1,size - index);elementData[index]= element;size++;}
类似的还有:
插入全部 addAll(Collection<? extends E> c)
从指定索引开始插入全部 addAll(int index, Collection<? extends E> c)
10. 移除某个索引上的元素remove(int index),返回被移除的元素
和上面的方法一样,需要整体移动数组容器;
同样的:
remove(Object o)方法是先找到对应元素的索引,然后整体移动
removeRange(int fromIndex, int toIndex) 移除索引fromIndex到toIndex的元素
11. 清空集合 clear()
循环给数组容器的元素赋值null
ArrayList中没有重写iterator()方法和listIterator()方法,继承的父类AbstractList中的方法.
2.1.2 LinkedList类
双向链表的数据结构.
类中有一个核心属性header,他是列表的头,初始化时会将上下节点都指向自身
publicclassLinkedList<E>extendsAbstractSequentialList<E>implementsList<E>,Deque<E>,Cloneable, java.io.Serializable{privatetransientEntry<E> header =newEntry<E>(null,null,null);privatetransientint size =0;publicLinkedList(){header.next = header.previous = header;}...
存放元素的容器为Entry对象,Entry是LinkedList的静态内部类,源码如下:
privatestaticclassEntry<E>{E element; //存放元素本身Entry<E> next; //指向下一个EntryEntry<E> previous; //上一个Entry//构造函数形参分别表示: 元素,下一个Entry,上一个Entry- Entry(E element,Entry<E> next,Entry<E> previous){
this.element = element;this.next = next;this.previous = previous;}}
插入数据核心方法 addBefore(E e,Entry<E> entry),在某个entry前插入元素:
privateEntry<E> addBefore(E e,Entry<E> entry){Entry<E> newEntry =newEntry<E>(e, entry, entry.previous);newEntry.previous.next = newEntry; //将上一个节点的下节点指向enewEntry.next.previous = newEntry; //将下一个节点的上节点指向esize++;modCount++;return newEntry;}
add(e)方法就是通过 addBefore(e,header)在header节点前插入 实现的
get(int index) 根据索引查找元素时会判断header正向和反向哪个离索引更近,然后向更近的方向遍历,源码如下:
//调用私有方法entry(i)找到对应索引的节点public E get(int index){return entry(index).element;}privateEntry<E> entry(int index){if(index <0|| index >= size)thrownewIndexOutOfBoundsException("Index: "+index+", Size: "+size);Entry<E> e = header;//>>位移运算,可以理解为除以2- //当所以在前半部分,就遍历下个节点,一直遍历到该索引的节点
- //在后半部分,就遍历上个节点
if(index <(size >>1)){for(int i =0; i <= index; i++)e = e.next;}else{for(int i = size; i > index; i--)e = e.previous;}return e;}
删除元素核心方法如下:
删除元素,不论是删除第一个还是最后一个,又或是根据索引删除,或者根据元素删除,步骤大致如下:
1.找到该元素对应的节点,然后调用remove(Object o)方法,该方法核心步骤主要做2,3两步操作;
2.让该节点的上下节点互相指向对方;
3.然后置空该节点的元素和上下节点应用;
注意:该方法会返回被删除元素
private E remove(Entry<E> e){if(e == header)thrownewNoSuchElementException();E result = e.element;//下面2行让上下节点互相指向对方e.previous.next = e.next;e.next.previous = e.previous;//置空被删除节点的属性e.next = e.previous =null;e.element =null;size--;modCount++;return result;}
LinkedList重写了listIterator(int index)方法,内部也存在个私有的内部类ListItr,和AbstractList抽象类中的实现类似,只是由操作索引换成了操作Entry节点,内部还有个nextIndex属性,记录下一个元素的索引,还有个next属性,记录下一个节点.
2.2 Set接口
首先,来看下Set接口源码:
package java.util;publicinterfaceSet<E>extendsCollection<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();}
没啥特殊的方法.基本全是Collection里面的.
AbstractSet抽象类
继承了AbstractCollection抽象类,实现了Set接口
里面就3个方法hashCode(),equals(),removeAll(ollection<?> c)
package java.util;publicabstractclassAbstractSet<E>extendsAbstractCollection<E>implementsSet<E>{protectedAbstractSet(){}publicboolean equals(Object o){if(o ==this)returntrue;if(!(o instanceofSet))returnfalse;Collection c =(Collection) o;if(c.size()!= size())returnfalse;try{return containsAll(c);}catch(ClassCastException unused){returnfalse;}catch(NullPointerException unused){returnfalse;}}publicint hashCode(){int h =0;Iterator<E> i = iterator();while(i.hasNext()){E obj = i.next();if(obj !=null)h += obj.hashCode();}return h;}publicboolean removeAll(Collection<?> c){boolean modified =false;if(size()> c.size()){for(Iterator<?> i = c.iterator(); i.hasNext();)modified |= remove(i.next()); // a |= b; 相当于 a = a | b;}else{for(Iterator<?> i = iterator(); i.hasNext();){if(c.contains(i.next())){i.remove();modified =true;}}}return modified;}}
2.2.1 HashSet类
继承了AbstractSet抽象类,实现了Set,Cloneable,和序列化接口
基于HashMap实现的,关于HashMap的介绍看 ★★集合框架--Map
package java.util;publicclassHashSet<E>extendsAbstractSet<E>implementsSet<E>,Cloneable, java.io.Serializable{staticfinallong serialVersionUID =-5024744406713321676L;privatetransientHashMap<E,Object> map;privatestaticfinalObject PRESENT =newObject();publicHashSet(){map =newHashMap<E,Object>();}publicHashSet(Collection<?extends E> c){map =newHashMap<E,Object>(Math.max((int)(c.size()/.75f)+1,16));addAll(c);}publicHashSet(int initialCapacity,float loadFactor){map =newHashMap<E,Object>(initialCapacity, loadFactor);}publicHashSet(int initialCapacity){map =newHashMap<E,Object>(initialCapacity);}HashSet(int initialCapacity,float loadFactor,boolean dummy){map =newLinkedHashMap<E,Object>(initialCapacity, loadFactor);}publicIterator<E> iterator(){return map.keySet().iterator();}publicint size(){return map.size();}publicboolean isEmpty(){return map.isEmpty();}publicboolean contains(Object o){return map.containsKey(o);}publicboolean add(E e){return map.put(e, PRESENT)==null;}publicboolean remove(Object o){return map.remove(o)==PRESENT;}publicvoid clear(){map.clear();}publicObject clone(){try{HashSet<E> newSet =(HashSet<E>)super.clone();newSet.map =(HashMap<E,Object>) map.clone();return newSet;}catch(CloneNotSupportedException e){thrownewInternalError();}}privatevoid writeObject(java.io.ObjectOutputStream s)throws java.io.IOException{s.defaultWriteObject();s.writeInt(map.capacity());s.writeFloat(map.loadFactor());s.writeInt(map.size());for(Iterator i=map.keySet().iterator(); i.hasNext();)s.writeObject(i.next());}privatevoid readObject(java.io.ObjectInputStream s)throws java.io.IOException,ClassNotFoundException{s.defaultReadObject();int capacity = s.readInt();float loadFactor = s.readFloat();map =(((HashSet)this)instanceofLinkedHashSet?newLinkedHashMap<E,Object>(capacity, loadFactor):newHashMap<E,Object>(capacity, loadFactor));int size = s.readInt();for(int i=0; i<size; i++){E e =(E) s.readObject();map.put(e, PRESENT);}}}

浙公网安备 33010602011771号