接下来的一段时间重点介绍java.util这个包中的内容,这个包厉害了,包含了collection与map,提供了集合、队列、映射等实现。一张图了解java中的集合类:

AbstractList

一、list简介

list是啥?为啥会有list的存在呢?java中的数组相信大家都是非常熟悉的,可以存放多个数据,但是数组有一个缺点,就是数组在创建之后,长度就不可更改(但是针对于数组的元素可以更改),若你需要在后续过程中往数组中添加数据,那麻烦了,不支持。

list在java中是collection(集合)的子接口,运行过程中可以增加元素或是减少元素。List里存放的对象是有序的(有序不是指按照元素的大小排列,是按照元素添加顺序为维度的,例如第一个被添加的元素,get的时候就第一个被拿出来),同时也是可以重复的。

二、AbstractList类定义

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>

protected AbstractList() {}

成员变量:

//代表修改次数
protected transient int modCount = 0;

三、主要方法

1、add()、addAll()

//把一个元素加进list
public boolean add(E e) {
//当然是调用指定index的方法,传入size,还有元素即可,假设一共5个元素,那么下标5一定是空的喽
add(size(), e);
return true;
}
//由各子类实现
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public boolean addAll(int index, Collection<? extends E> c) {
//校验index是否<0或>list.size()
rangeCheckForAdd(index);
boolean modified = false;
for (E e : c) {
//循环添加元素
add(index++, e);
modified = true;
}
return modified;
}

2、get()、set()、remove()

//都是由子类实现
//获取对应索引位的元素
abstract public E get(int index);
//更改给定索引位的元素
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
//根据某个索引删除该索引位的元素
public E remove(int index) {
throw new UnsupportedOperationException();
}

3、indexOf()、lastIndexOf()

public int indexOf(Object o) {
//使用listIterator迭代器
ListIterator<E> it = listIterator();
if (o==null) {
while (it.hasNext())
//在执行next()方法时,取的是当前对象,并且cursor+1,可以看后面内部类的介绍
if (it.next()==null)
//由于cursor+1,所以要取前一个索引值
return it.previousIndex();
} else {
while (it.hasNext())
//如果元素不为null的话,使用equals()进行比较。因为null.equals()会报空指针
if (o.equals(it.next()))
return it.previousIndex();
}
//若list没有此元素,则返回-1
return -1;
}
//倒着循环list,就ok了
public int lastIndexOf(Object o) {
ListIterator<E> it = listIterator(size());
if (o==null) {
while (it.hasPrevious())
if (it.previous()==null)
return it.nextIndex();
} else {
while (it.hasPrevious())
if (o.equals(it.previous()))
return it.nextIndex();
}
return -1;
}

4、clear()

//清空整个list
public void clear() {
removeRange(0, size());
}
//将fromIndex到toIndex范围内的元素全干掉
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();
}
}

5、subList()

public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<>(this, fromIndex, toIndex) :
new SubList<>(this, fromIndex, toIndex));
}

6、equals()、hashCode()

public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof 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();
//比较的逻辑就在这了,如果都为null,就相等,否则就equals比较
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
//这地方也很好理解,比如两个list,一个5个长度,一个6个长度,并且前5个元素都相等,前面的玄幻已经干完5个元素的比较了。那就判断是否还有下一个,就可以得出他们长度是否相等了
return !(e1.hasNext() || e2.hasNext());
}
public int hashCode() {
int hashCode = 1;
for (E e : this)
//为啥乘以31?31是质子数中一个“不大不小”的存在,如果你使用的是一个如2的较小质数,那么得出的乘积会在一个很小的范围,很容易造成哈希值的冲突。而如果选择一个100以上的质数,得出的哈希值会超出int的最大范围,这两种都不合适。
而如果对超过 50,000 个英文单词(由两个不同版本的 Unix 字典合并而成)进行 hash code 运算,并使用常数 31, 33, 37, 39 和 41 作为乘子,每个常数算出的哈希值冲突数都小于7个(国外大神做的测试),那么这几个数就被作为生成hashCode值得备选乘数了。
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}

四、迭代器Iterator

迭代器是什么东西呢?为啥循环list的时候需要用到它?其实原因也很简单,迭代器其实是一种设计模式,在java中,list的实现有很多种,例如数组列表(ArrayList)、链表结构的(LinkedList)等等。那么循环这些不同数据结构的list就要命了,因此呢,使用Iterator封装循环获取list的方法,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。

1、iterator()、listIterator()

上面的代码中经常出现listIterator()方法

    //Itr以及ListItr是两个内部类
public Iterator<E> iterator() {
return new Itr();
} public ListIterator<E> listIterator() {
return listIterator(0);
}
public ListIterator<E> listIterator(final int index) {
//检查索引是否<0或>list.size
rangeCheckForAdd(index); return new ListItr(index);
}

2、Itr

private class Itr implements Iterator<E> {
//在调用next()之后,cursor+1(因为最终cursor与size做比较的,cursor=index+1)随后返回的下标
int cursor = 0;
//调用next、previous,都会更新该下标值,当调用remove元素,就会重置为-1,代表最后返回的元素下标
int lastRet = -1;
////将Abstract修改次数赋值给预期次数
int expectedModCount = modCount;
//看到这里就好理解上面了,cursor比较的是size。例如list有5个元素,size为5,index为0-4
public boolean hasNext() {
return cursor != size();
} public E next() {
//检查修改次数
checkForComodification();
try {
int i = cursor;
//获取当前元素
E next = get(i);
//设置为当前索引
lastRet = i;
//获取到当前元素之后,cursor=cursor+1
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
} public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); try {
//内部类调用外部类的方法,类名.this.xxx(),删除的是next()调用时的元素
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
//删除了一个元素,cursor自然要-1
cursor--;
//将lastRet重置为-1
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

3、ListItr

//这玩意扩展了Itr,可以倒着循环
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
//这里面的cursor就等于index。跟Itr不同
cursor = index;
} public boolean hasPrevious() {
return cursor != 0;
} public E previous() {
checkForComodification();
try {
//取前一个元素,这里的cursor跟index一致。当前index-1
int i = cursor - 1;
E previous = get(i);
lastRet = cursor = i;
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(); try {
int i = cursor;
AbstractList.this.add(i, e);
//添加元素也将lastRet设为-1
lastRet = -1;
cursor = i + 1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}

为啥先介绍AbstractList?因为后面的重点ArrayList以及LinkedList都是他的子类,所以后面学起来就能串起来了,毕竟用了很多父类的功能。

走进JDK(五)------AbstractList的更多相关文章

  1. 调试过程中发现按f5无法走进jdk源码

    debug 模式 ,在fis=new FileInputStream(file); 行打断点 调试过程中发现按f5无法走进jdk源码 package com.lzl.spring.test; impo ...

  2. 走进JDK(十)------HashMap

    有人说HashMap是jdk中最难的类,重要性不用多说了,敲过代码的应该都懂,那么一起啃下这个硬骨头吧!一.哈希表在了解HashMap之前,先看看啥是哈希表,首先回顾下数组以及链表数组:采用一段连续的 ...

  3. 走进JDK(二)------String

    本文基于java8. 基本概念: Jvm 内存中 String 的表示是采用 unicode 编码 UTF-8 是 Unicode 的实现方式之一 一.String定义 public final cl ...

  4. 走进JDK(八)------AbstractSet

    说完了list,再说说colletion另外一个重要的子集set,set里不允许有重复数据,但是不是无序的.先看下set的整个架构吧: 一.类定义 public abstract class Abst ...

  5. 走进JDK(七)------LinkedList

    要学习LinkedList,首先得了解链表结构.上篇介绍ArrayList的文章中介绍了底层是数组结构,查询快的问题,但是删除时,需要将删除位置后面的元素全部左移,因此效率比较低. 链表则是这种机制: ...

  6. 走进JDK(六)------ArrayList

    对于广大java程序员来说,ArrayList的使用是非常广泛的,但是发现很多工作了好几年的程序员不知道底层是啥...这我觉得对于以后的发展是非常不利的,因为java中的每种数据结构的设计都是非常完善 ...

  7. 走进JDK(一)------Object

    阅读JDK源码也是一件非常重要的事情,尤其是使用频率最高的一些类,通过源码可以清晰的清楚其内部机制. 如何阅读jdk源码(基于java8)? 首先找到本地电脑中的jdk安装路径,例如我的就是E:\jd ...

  8. 走进AngularJs(五)自定义指令----(下)

    自定义指令学习有段时间了,学了些纸上谈兵的东西,还没有真正的写个指令出来呢...所以,随着学习的接近尾声,本篇除了介绍剩余的几个参数外,还将动手结合使用各参数,写个真正能用的指令出来玩玩. 我们在自定 ...

  9. 走进JDK(十二)------TreeMap

    一.类定义 TreeMap的类结构: public class TreeMap<K,V> extends AbstractMap<K,V> implements Navigab ...

随机推荐

  1. arcgis10.2 打开CAD文件注记乱码

    1.使用ARCGIS10.2打开CAD文件,图面显示的注记内容为乱码,属性表中的注记内容正常2.同样的CAD文件在ARCGIS9.3中打开正常出现此情况影响历史数据使用,请求ESRI技术支持注:系统添 ...

  2. word embeddding和keras中的embedding

    训练好的词向量模型被保存下来,该模型的本质就是一个m*n的矩阵,m代表训练语料中词的个数,n代表训练时我们设定的词向量维度.当我们训练好模型后再次调用时,就可以从该模型中直接获取到对应词的词向量. 通 ...

  3. Northwind数据库练习及参考答案

    --查询订购日期在1996年7月1日至1996年7月15日之间的订单的订购日期.订单ID.客户ID和雇员ID等字段的值 Create View Orderquery as Select OrderDa ...

  4. python学习笔记之四-多进程&多线程&异步非阻塞

    ProcessPoolExecutor对multiprocessing进行了高级抽象,暴露出简单的统一接口. 异步非阻塞 爬虫 对于异步IO请求的本质则是[非阻塞Socket]+[IO多路复用]: & ...

  5. vi 常用 文本编辑 技巧

    归纳常用的Vi/Vim 文本编辑技巧,便于以后查阅. 一.把空格替换为换行 :% s/ /\r/g 二.把空行删除 :g/^$/d 三.vim以16进制打开和编辑文件 先用vim以二进制格式打开需要编 ...

  6. ubuntu server cloud img username password

    新安装了OpenStack Queens发现无镜像,蹦蹦跳跳的下载了ubuntu的镜像 网址https://cloud-images.ubuntu.com/ 最好你自己找你想要的,vmdk.ova.i ...

  7. OpenStack Mitaka/Newton/Ocata/Pike 各版本功能贴整理

    逝者如斯,刚接触OpenStack的时候还只是第9版本IceHouse.前几天也看到了刘大咖更新了博客,翻译了Mirantis博客文章<OpenStack Pike 版本中的 53 个新功能盘点 ...

  8. pyton unittest

    在说unittest之前,先说几个概念: TestCase 也就是测试用例 TestSuite 多个测试用例集合在一起,就是TestSuite TestLoader是用来加载TestCase到Test ...

  9. Servlet-知识点

    2018年10月05日 16:52:56 yigg 阅读数:38   1.JavaWeb开发的目录结构 https://blog.csdn.net/u012661010/article/details ...

  10. MaC 修改MySQL密码

    1.苹果->系统偏好设置->最下边点mysql 在弹出页面中 关闭mysql服务(点击stop mysql server) 2.进入终端输入:cd /usr/local/mysql/bin ...