ArrayList

今天我们来详细看一下ArrayList的源码.首先我们来看一下ArrayList的实现和继承

public class ArrayList<E>extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable

面试常问问题!

为什么ArrayList查询比较快?
1.底层的数据结构
2.RandomAccess 快速访问 只是其标记作用

一、构造方法

  • 用指定的大小来初始化内部数组
public ArrayList(int initialCapacity)  // 阿里巴巴手册推荐使用 10
  • 默认构造器,以默认大小来初始化内部数组
public ArrayList()
  • 接收一个 Collection 对象,并将该集合的元素添加到 ArrayList 中
public ArrayList(Collection<? extends E> c)

二、常用方法

boolean add(E e)                     //将指定的元素追加到此列表的末尾。
void add(int index, E element)      //在此列表中的指定位置插入指定的元素
E get(int index)                    //返回此列表中指定位置的元素。
E remove(int index)                 //删除该列表中指定位置的元素。
boolean remove(Object o)            //从列表中删除指定元素的第一个出现(如果存在)
E set(int index, E element)         //用指定的元素替换此列表中指定位置的元素。 //返回删除元素
List<E> subList(int fromIndex, int toIndex)  //返回此列表中指定的 fromIndex (包括)和 toIndex之间的独占视图。

具体的方法代码就不写了小伙伴们可以自己试一下,这里着重讲解一下subList这一个方法

2.1subList方法

   List<Integer> list = new ArrayList<>(10);list.add(100);list.add(200);list.add(300);list.add(400);List<Integer> subList = list.subList(0, 2);subList.add(222);System.out.println("list:"+list);// list:[100, 200, 222, 300, 400]System.out.println("subList:"+subList);// subList:[100, 200, 222]
//如上代码所示当我再subList集合中添加一个222元素时,list集合中也会出现222
//说明subList与list集合公用一块内存
//对subList集合的add/delete/update都会映射源集合list上面ArrayList<Integer> integers = new ArrayList<>(subList);integers.add(222);list.add(123);System.out.println("integers"+integers); //integers:[100, 200, 222]System.out.println("list"+list); //list:[100, 200, 300, 400, 123]
//如果一个List用了subList方法的话那么如果对源list集合做任何修改、删除、添加的操作
//都会触发ConcurrentModificationException异常
//解决方法-------------------------subList = new ArrayList<>(subList);
//通过ArrayList的有参构造赋重新赋值subList

三、 add()源码

首先来一波语言概述:
ArrayList的构方法并没有给数组初始化空间,这个动态数组的初始化时间是在创建对后执行第一次add方法后才出现的。
首先判断集合元素的数组是否为空ensureCapacityInternal方法内的calculateCapacity方法,为空的话就是第一次初始化,返回DEFAULT_CAPACITY的值10。然后进入方法ensureExplicitCapacity进行扩容第一次扩容为0,扩容的新数组和返回过来的10做对比如果为负数就赋值给新数组然后用Arrats.copyOf方法将原数组元素elementData复制到扩容后的数组然后再赋值到原数组。接着赋值传入的数据给 elementData[size++]
当添加第11个元素时进行第二次扩容

第一步:创建数组(这里的创建数组不指定初始大小)

 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};   // eg1:初始化ArrayList实例,则elementData={} public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}

第二步: add一个内容到数组中

// private int size; 维护集合的元素个数
public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!  扩容 elementData[size++] = e;  //存值(size每次指向待插入的位置)size最开始是0。所以elementData[0] = e,然后size再+1return true;  //返回true }

第三步:进入扩容方法ensureCapacityInternal

    // eg1:第一次新增元素,所以size=0,则:minCapacity=size+1=1private void    ensureCapacityInternal(int minCapacity) {// eg1:第一次新增元素,calculateCapacity方法返回值为DEFAULT_CAPACITY=10ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}

第四步:进入比较大小方法calculateCapacity

    // eg1:第一次新增元素,elementData={} minCapacity=1private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 如果是第一次add的话才会进入if语句.// eg1:满足if判断,DEFAULT_CAPACITY=10return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;}

第五步:进入判断是否扩容方法ensureExplicitCapacity

    // eg1:第一次新增元素,minCapacity=10private void ensureExplicitCapacity(int minCapacity) {// eg1: modCount++后,modCount=1modCount++;/** 如果所需的最小容量大于elementData数组的容量,则进行扩容操作 */if (minCapacity - elementData.length > 0) { // eg1:10-0=10,满足扩容需求// eg1:minCapacity=10grow(minCapacity);}}

第六步:进行扩容方法grow

 // eg1:第一次新增元素,minCapacity=10,即:需要将elementData的0长度扩容为10长度。private void grow(int minCapacity) {/** 原有数组elementData的长度*/int oldCapacity = elementData.length; // eg1:oldCapacity=0/*** A >> 1 等于 A/2* eg: 3 >> 1 = 3/2 = 1*     4 >> 1 = 4/2 = 2* ------------------------* A << 1 等于 A*2* eg: 3 << 1 = 3*2 = 6*     4 << 1 = 4*2 = 8** 000100 >> 1 = 000010* 000100 << 1 = 001000*//** 新增oldCapacity的一半整数长度作为newCapacity的额外增长长度 */int newCapacity = oldCapacity + (oldCapacity >> 1); // eg1:newCapacity=0+(0>>1)=0/** 新的长度newCapacity依然无法满足需要的最小扩容量minCapacity,则新的扩容长度为minCapacity */if (newCapacity - minCapacity < 0) {// eg1:newCapacity=10newCapacity = minCapacity;}/** 新的扩容长度newCapacity超出了最大的数组长度MAX_ARRAY_SIZE */if (newCapacity - MAX_ARRAY_SIZE > 0) {newCapacity = hugeCapacity(minCapacity);}/** 扩展数组长度为newCapacity,并且将旧数组中的元素赋值到新的数组中 */// eg1:newCapacity=10, 扩容elementData的length=10elementData = Arrays.copyOf(elementData, newCapacity);}

说了这么多还是不太理解怎么办、除了自己走一遍流程才能清楚明白。最好的方法就是看图片了。

四、 remove()源码

首先判断删除元素是否为null然后进入fastRemove方法删除元素获取  int numMoved = size - index - 1;的值如果大于0就 System.arraycopy操作,将index后面的元素向前位移一位。之后size的值置为null删除成功

如果使用remove()建议通过索引删除

 public boolean remove(Object o) {if (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {fastRemove(index);return true;}} else {for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {fastRemove(index);return true;}}return false;}
private void fastRemove(int index) {//删除元素索引modCount++;//int numMoved = size - index - 1;if (numMoved > 0)//判断索引后面是否还存在元素  元素移动位置System.arraycopy(elementData, index+1, elementData, index,numMoved);//如果删除的元素为最后 一个就不执行 System.arraycopy操作elementData[--size] = null; // clear to let GC do its work}

五、 get()源码

public E get(int index) {rangeCheck(index);//检测index范围return elementData(index);//直接返回元素索引}

六、其他问题

为什么Arraylist通过循环删除不了第一位置的元素而能删除倒数第二个元素

1.首先我们来看一下集合元素的第一个不能删除的情况ArrayList<Integer> list = new ArrayList<>(10);list.add(100);list.add(200);list.add(300);list.add(400);for (Integer integer : list) {if(integer.equals(100))list.remove(integer);}System.out.println(list);
=====================当我们执行以上操作的时候报异常**Exception in thread "main" java.util.ConcurrentModificationException**
2.删除倒数第二个元素for (Integer integer : list) {if(integer.equals(300))list.remove(integer);}//[100, 200, 400]成功

集合快速失败

fail-fast 迭代器设计模式;modCount----expectedModeCount最后一个不遍历了:hasNext中的cursor==size所以可以删除第二个cursor值累加size减少条件满足没有执行后面的代码所以没有抛出异常,

1.循环方法

============================普通for循环ArrayList<Integer> list = new ArrayList<>(10);list.add(23);list.add(12);list.add(54);list.add(13);for (int i = 0; i < list.size(); i++) {System.out.println("元素"+i+":"+list.get(i));}
============================================================
ListIterator<Integer> integerListIterator = list.listIterator();while (integerListIterator.hasNext()){Integer next = integerListIterator.next();System.out.println(next);integerListIterator.remove();//的删除功能integerListIterator.add(200);//listIterator的新增功能}===============================================================Iterator<Object> iterator = list.iterator();//集合的每个元素while (iterator.hasNext()){Object next = iterator.next();System.out.println(next);}
===============================================================list.forEach((element)->{System.out.println(element);});

2. 删除集合中多个元素

===========================================正常for循环删除
for (int i = 0; i < list.size(); i++) {if(Integer.valueOf(12).equals(list.get(i))){list.remove(i);i--;}}
===========================================removeIf删除
list.removeIf(s->Integer.valueOf(12).equals(s));    

3. 排序

 list.sort(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o1.compareTo(o2);}});Collections.sort(list);

ArrayList学习[常用方法|源码]相关推荐

  1. 面试官系统精讲Java源码及大厂真题 - 04 Arrays、Collections、Objects 常用方法源码解析

    04 Arrays.Collections.Objects 常用方法源码解析 读一本好书,就是和许多高尚的人谈话. --歌德 引导语 我们在工作中都会写工具类,但如何才能使写出来的工具类更好用,也是有 ...

  2. 学习hutool源码TreeUtil.build()得到了什么

      目录 概述 考虑的问题 hutool具体实现 1.下面是hutool管网提供的安例. 2.TreeUtil.build()做了什么 3.build()方法 4.innerBuild()方法 学到了 ...

  3. 转载:深入学习java源码之Callable.call()与Future.get()

    原始链接:https://blog.csdn.net/qq_35029061/article/details/86750369 深入学习java源码之Callable.call()与Future.ge ...

  4. 免费学习机器学习和深度学习的源码、学习笔记和框架分享

    机器学习和深度学习的免费学习源码.学习笔记和框架分享 python笔记 源码 python导入模块的的几种方式 在python中,字典按值排序 python中set的基本常用方法 python取出fr ...

  5. 深入学习java源码之 Arrays.sort()与Arrays.parallelPrefix()

    深入学习java源码之 Arrays.sort()与Arrays.parallelPrefix() Comparator接口 能对不同类型的对象进行排序(当然排序依据还是基本类型),也不用自己实现排序 ...

  6. vue filter对象_学习vue源码(3) 手写Vue.directive、Vue.filter、Vue.component方法

    一.Vue.directive Vue.directive(id,[definition]); 1)参数 { string } id{ Function | Object } [ definition ...

  7. 我该如何学习spring源码以及解析bean定义的注册

    如何学习spring源码 前言 本文属于spring源码解析的系列文章之一,文章主要是介绍如何学习spring的源码,希望能够最大限度的帮助到有需要的人.文章总体难度不大,但比较繁重,学习时一定要耐住 ...

  8. TLD(Tracking-Learning-Detection)学习与源码理解之(六)

    TLD(Tracking-Learning-Detection)学习与源码理解之(六) zouxy09@qq.com http://blog.csdn.net/zouxy09 下面是自己在看论文和这些 ...

  9. TLD(Tracking-Learning-Detection)学习与源码理解之(五)

    TLD(Tracking-Learning-Detection)学习与源码理解之(五)   zouxy09@qq.com http://blog.csdn.net/zouxy09 下面是自己在看论文和 ...

  10. TLD(Tracking-Learning-Detection)学习与源码理解之(四)

    TLD(Tracking-Learning-Detection)学习与源码理解之(四) zouxy09@qq.com http://blog.csdn.net/zouxy09 下面是自己在看论文和这些 ...

最新文章

  1. Java多线程编程的常见陷阱
  2. C/C++笔试、面试题
  3. Python: 编程遇到的一些问题以及网上解决办法?
  4. 铝电解电容总结[转]
  5. 查看sqlserver信息
  6. 【JZOJ 5421】【NOIP2017提高A组集训10.25】嘟嘟噜
  7. I2P和TOR 有趣网络精灵
  8. oa项目环境搭建的操作步骤详解
  9. 小米手环无法模拟门卡_小米手环3门禁卡设置方法 居然可以模拟门禁卡
  10. 查看进程及结束进程命令
  11. 穿越火线怎么在服务器显示人数,现在穿越火线每天平均有多少人在线?
  12. 条码软件为什么要有等级要求(A级条码)
  13. Redist过期策略、应用、持久化
  14. 【Java】棋盘覆盖问题
  15. 游戏性的根本——浅谈游戏关卡设计
  16. php数字转人民币,php人民币数字转大写数字的简单示例
  17. 《Access 2007开发指南(修订版)》一一1.9 Access 2007的新功能
  18. 刷脸支付基于人脸识别系统的新支付方式
  19. 中文的游戏配音一定不好吗?
  20. Spring的事务处理

热门文章

  1. 从零开始进行Adadelta的梯度下降
  2. Pyinstaller打包过程中报错“AttributeError: 'str' object has no attribute 'items''”问题解决
  3. c++ 协程_深入理解异步I/O+epoll+协程
  4. 合肥师范学院计算机操作系统期末考试题,2005级操作系统期末试卷A卷及答案
  5. 基于SSM的NBA篮球球队运营系统
  6. thymeleaf使用
  7. javascript 弹出层(警告框)的制作(css元素居中、javascript元素居中)
  8. Logstash 日志搜集处理框架 安装配置
  9. 阶段5 3.微服务项目【学成在线】_day01 搭建环境 CMS服务端开发_13-MongoDb入门-数据库和集合...
  10. 阶段3 2.Spring_01.Spring框架简介_05.spring的优势