原文链接:
简化体系图
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);//返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1
int lastIndexOf(Object o);//返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1
ListIterator<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); //在当前游标索引处插入元素,然后光标自增1
lastRet =-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. 扩容 public void 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 Cloneable
thrownewInternalError();
}
}
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; //指向下一个Entry
Entry<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; //将上一个节点的下节点指向e
newEntry.next.previous = newEntry; //将下一个节点的上节点指向e
size++;
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;
- //当所以在前半部分,就遍历下个节点,一直遍历到该索引的节点
- //在后半部分,就遍历上个节点
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);
}
}
}