Java源码初学_AbstractList&AbstractCollection
一.AbstractCollection抽象类:(提供了Collection接口的骨干实现,以减少实现接口所需要的工作)
1.contains方法
contains方法,通过迭代器对于列表的每一个元素进行遍历,并且判断是否与给定的元素相等.另外由于传入的元素可能为null,因此在执行传入的元素的equals方法的时候,需要先判断是否为null.源代码如下:
public boolean contains(Object o) {
Iterator<E> it = iterator();
if (o==null) {
while (it.hasNext()) //迭代器的hasNext的实现通常是拿索引和容器容量作比较.因此当容器为null时,返回的是false
if (it.next()==null) //只有当迭代的元素为null时返回true,
return true;
} else {
while (it.hasNext()) //进行迭代.
if (o.equals(it.next())) //调用equals方法判断(传入的对象的equals方法)
return true;
}
return false;//集合为null或者没有找到对应的元素的时候
}
2.toArray()方法
空参的toArray方法,在方法内部定义了一个Object数组,并且对于容器内部的元素采用迭代器进行遍历,并且将每个元素放置于Object数组中.
public Object[] toArray() {
// 估计数组的大小;可能会有更多或者更少的元素
Object[] r = new Object[size()];
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) // 比预想的元素要少
return Arrays.copyOf(r, i);
r[i] = it.next();
}
return it.hasNext() ? finishToArray(r, it)/*比预想的元素多*/ : r;
}
3.toArray(T[])方法
这个方法如果给定的数组够大,则将结束索引的元素设为null,否则建立一个新的数组并且返回.
public <T> T[] toArray(T[] a) {
/* 将数组的大小设为容器的大小
参数指定的数组大于容器的大小,则在参数的数组设置值
否则new一个新的数组
getComponentType返回的是数组组件的类型
*/int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { //比预期的更少
if (a == r) {
r[i] = null; // 将索引i处的位置设为null
} else if (a.length < i) {
return Arrays.copyOf(r, i);//返回copy的数组(重新新建立一个数组)
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;//将索引i处的位置设为null
}
}
return a;
}
r[i] = (T)it.next();
}
//比预期的元素更多
return it.hasNext() ? finishToArray(r, it) : r;
}
private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
int i = r.length;
while (it.hasNext()) {
int cap = r.length;
if (i == cap) {
int newCap = cap + (cap >> 1) + 1;
if (newCap - MAX_ARRAY_SIZE > 0)
newCap = hugeCapacity(cap + 1);
r = Arrays.copyOf(r, newCap);//扩充容量
}
r[i++] = (T)it.next();
}
// 如果i与r.length相等,返回r否则返回copy的数组
return (i == r.length) ? r : Arrays.copyOf(r, i);
}
4.addAll方法
addAll方法:直接对Collection中的每个元素进行add方法的调用,只要调用成功了则返回true.
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;//只要添加成功,modified设为true
return modified;
}
二.AbstractList类
此类提供 List 接口的骨干实现,以最大限度地减少实现“随机访问”数据存储(如数组)支持的该接口所需的工作。对于连续的访问数据(如链表),应优先使用 AbstractSequentialList,而不是此类.
要实现可修改的列表,编程人员必须另外重写 set(int, E) 方法(否则将抛出UnsupportedOperationException)。如果列表为可变大小,则编程人员必须另外重写 add(int, E) 和 remove(int) 方法。
例如在AbstractList中,set方法是这样实现的:
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
1.indexOf方法
和contains方法实现几乎相同,但是采用了ListIterator对容器内部的元素进行了遍历:
public int indexOf(Object o) {
ListIterator<E> it = listIterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return it.previousIndex();
} else {
while (it.hasNext())
if (o.equals(it.next()))
return it.previousIndex();//此时索引已经加1.需要用privioutsIndex来获取索引
}
return -1;
}
2.clear方法
clear方法采用ListIterator来实现对于指针经过的元素进行删除:
public void clear() {
removeRange(0, size());//改变容量大小的操作
}
protected void removeRange(int fromIndex, int toIndex) {
ListIterator<E> it = listIterator(fromIndex);
for (int i=0, n=toIndex-fromIndex; i<n; i++) {
it.next();
it.remove();
}
}
3.迭代器内部实现:
在AbstractList中定义了内部类,实现了迭代器接口,实现了对于元素的遍历.Itr(普通迭代器)的实现,在迭代器内部定义了一个指针,每次返回元素后,指针将会指向下一个元素,继续返回,直到计数器达到容器的大小.同时迭代器的实现也解释了不能还没有调用next,就remove,也不能连续调用两次remove方法的原因:未调用next就remove,由于lastret值为-1,会报异常,而在第一次调用remove后,lastret值变为-1,如果再次调用remove也会发生异常!
private class Itr implements Iterator<E> {
/**
* 下一个元素的索引
*/
int cursor = 0;
/**
* 前一个元素的索引
*/
int lastRet = -1;
/**
* 记录容器被修改的次数
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();//检查遍历的时候容器有没有被修改过
try {
int i = cursor;
E next = get(i);//获取元素
lastRet = i; //lastRet记录获取到的元素的索引
cursor = i + 1;//cursor+1,准备获取接下来的元素
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0) //这行代码解释了,不能未调用next()就remove() 也不能连续调用两次remove()
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ListIterator的实现,和Iterator实现基本相同,它继承了Itr类,区别在于它支持在调用next或者privious后,添加元素.这里根据源代码分析.在调用next的时候,调用add,add方法会在cursor指针(下一个要next的元素)的位置添加元素.并将cursor+1,使得next的调用无法返回添加的元素.在调用privious的时候,调用add,add方法会在已经返回的元素的位置处,添加元素,并将cursor+1,这时候,下次返回的将是cursor-1的元素,即新添加的元素.
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
cursor = index; //将元素指针设为指定值
}
public boolean hasPrevious() {
return cursor != 0;
}
public E previous() {
checkForComodification(); //检查是否被修改
try {
int i = cursor - 1;
E previous = get(i);
lastRet = cursor = i;//结束该方法调用的时候,cursor位置停留在返回的元素的位置上,这点与next不同
return previous;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor-1;
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.set(lastRet, e);
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
/*
add方法在next后调用下一个next无法返回新添加的元素
在privious后调用,下一个privious可以返回.
实际上无论是在next还是在privious方法后调用,
add方法都使得指针向前移动了1位.
*/
try {
int i = cursor;
AbstractList.this.add(i, e);
lastRet = -1;
cursor = i + 1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
4.equals方法的实现:
equals方法采用ListIterator方法对于每一个元素都进行了遍历,在调用ListIerator方法之前,需要先判断是否指向同一对象或者是否运行时类相同.
public boolean equals(Object o) {
if (o == this) //是否指向同一个对象
return true;
if (!(o instanceof List)) //是否都是List对象
return false;
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext()); //如果有一方还有多余的元素,那么判断不成立
}
5.subList方法
该方法new了一个List并且在这里List里持有源容器对象的引用.可以通过更改这个List,更改原来的容器(这样的List又称为源容器的视图),offset索引指示了视图与容器的偏移量.
/*
该方法new了一个List并且在这里List里持有源容器对象的引用.
可以通过更改这个List,更改原来的容器
*/
public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<>(this, fromIndex, toIndex) :
new SubList<>(this, fromIndex, toIndex));
}
class SubList<E> extends AbstractList<E> {
private final AbstractList<E> l;//持有原容器对象的引用
private final int offset;
private int size; SubList(AbstractList<E> list, int fromIndex, int toIndex) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > list.size())
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
l = list;
offset = fromIndex;
size = toIndex - fromIndex;
this.modCount = l.modCount;
} public E set(int index, E element) {
rangeCheck(index);
checkForComodification();
return l.set(index+offset, element);//可以通过更改subList来设置原容器对象
}
....}
Java源码初学_AbstractList&AbstractCollection的更多相关文章
- Java源码初学_HashSet&LinkedHashSet
一.概述 HashSet是建立在HashMap的基础上的,其内部存在指向一个HashMap对象的引用,操作HashSet实际上就是操作HashMap,而HashMap中所有键的值都指向一个叫做Dumm ...
- Java源码初学_LinkedHashMap
一.概述: LinkedHashMap是HashMap的子类,它的基本操作与HashMap相同,与之不同的是,它可以实现按照插入顺序进行排序.也可以通过在构造函数中指定参数,按照访问顺序排序(Link ...
- Java源码初学_HashMap
一.概念 HashMap的实例有两个参数影响其性能:初始容量和加载因子.容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量.加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度.当哈希表 ...
- Java源码初学_LinkedList
一.LinkedList的内部数据结构 LinkedList底层是一个链表的数据结构,采用的是双向链表,基本的Node数据结构代码如下: private static class Node<E& ...
- Java源码初学_ArrayList
一.ArrayList的构造器和构造方法 在ArrayList中定义了两个空数组,分别对应当指定默认构造方法时候,指向的数组已经给定容量,但是容量等于0的时候,指向的数组.此外在构造函数中传入Coll ...
- 如何阅读Java源码 阅读java的真实体会
刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比 ...
- Android反编译(一)之反编译JAVA源码
Android反编译(一) 之反编译JAVA源码 [目录] 1.工具 2.反编译步骤 3.实例 4.装X技巧 1.工具 1).dex反编译JAR工具 dex2jar http://code.go ...
- 如何阅读Java源码
刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动.源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧, ...
- Java 源码学习线路————_先JDK工具包集合_再core包,也就是String、StringBuffer等_Java IO类库
http://www.iteye.com/topic/1113732 原则网址 Java源码初接触 如果你进行过一年左右的开发,喜欢用eclipse的debug功能.好了,你现在就有阅读源码的技术基础 ...
随机推荐
- js获取随机色
方法一: var getRandomColor = function(){ return '#' + (function(color){ return (color += '0123456789abc ...
- cookie学习
cookie是储存于访问者的计算机中的变量,每当同一台计算机通过浏览器请求某个页面时,就会发送这个cookie,可以使用javascript来创建和取回cookie的值. 创建和存储cookie 首先 ...
- SQL SERVER 数据库实用SQL语句
--查看指定表的外键约束 select * from sysobjects where parent_obj in( select id from sysobjects where name='表名' ...
- Windows下Python,setuptools,pip,virtualenv的安装
Windows 2003平台,安装Python2.7.4,Python3.3,setuptools,pip,virtualenv. 安装Python2.7.4(当前版本是2.7.6),安装路径:C:\ ...
- Rigidbody SweepTest测试
和Physics的投射差不多,SweepTest可以直接投射当前碰撞 但是比较遗憾的是它对MeshCollider的支持不是很好,需要勾选Convex 投射和Physics一样,只要加了碰撞器,不管勾 ...
- 使用自定义材质球,实现NGUI屏幕溶解和灰显
UITexture实现的溶解: 重设UITeture的材质球实现上述效果,把当前屏幕渲染的Texture2D丢给UITexture,即可实现UI屏幕特效,背景模糊等都可以. 难点主要是实时刷新问题 解 ...
- Poj(2771),最大独立集
题目链接:http://poj.org/problem?id=2771 Guardian of Decency Time Limit: 3000MS Memory Limit: 65536K To ...
- hiho(1081),SPFA最短路,(非主流写法)
题目链接:http://hihocoder.com/problemset/problem/1081 SPFA求最短路,是不应-羁绊大神教我的,附上头像. 我第一次写SPFA,我用的vector存邻接表 ...
- 性能tips
Latch 闩 锁的平级 采样时间不能太长,太频繁 一般情况下,性能图应该是一种趋势图,看的是趋势,不看某些单个点 在压测收集数据时,可能多种工具收集到的性能数据有少许差异,原因: 网络延迟,导致收集 ...
- JDBC连接属性
JDBC连接属性 hibernate.connection.driver_classs属性:设置连接数据库的驱动: hibernate.connection.url属性:设置所需连接数据库的URL: ...