ArrayList学习[常用方法|源码]
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学习[常用方法|源码]相关推荐
- 面试官系统精讲Java源码及大厂真题 - 04 Arrays、Collections、Objects 常用方法源码解析
04 Arrays.Collections.Objects 常用方法源码解析 读一本好书,就是和许多高尚的人谈话. --歌德 引导语 我们在工作中都会写工具类,但如何才能使写出来的工具类更好用,也是有 ...
- 学习hutool源码TreeUtil.build()得到了什么
目录 概述 考虑的问题 hutool具体实现 1.下面是hutool管网提供的安例. 2.TreeUtil.build()做了什么 3.build()方法 4.innerBuild()方法 学到了 ...
- 转载:深入学习java源码之Callable.call()与Future.get()
原始链接:https://blog.csdn.net/qq_35029061/article/details/86750369 深入学习java源码之Callable.call()与Future.ge ...
- 免费学习机器学习和深度学习的源码、学习笔记和框架分享
机器学习和深度学习的免费学习源码.学习笔记和框架分享 python笔记 源码 python导入模块的的几种方式 在python中,字典按值排序 python中set的基本常用方法 python取出fr ...
- 深入学习java源码之 Arrays.sort()与Arrays.parallelPrefix()
深入学习java源码之 Arrays.sort()与Arrays.parallelPrefix() Comparator接口 能对不同类型的对象进行排序(当然排序依据还是基本类型),也不用自己实现排序 ...
- vue filter对象_学习vue源码(3) 手写Vue.directive、Vue.filter、Vue.component方法
一.Vue.directive Vue.directive(id,[definition]); 1)参数 { string } id{ Function | Object } [ definition ...
- 我该如何学习spring源码以及解析bean定义的注册
如何学习spring源码 前言 本文属于spring源码解析的系列文章之一,文章主要是介绍如何学习spring的源码,希望能够最大限度的帮助到有需要的人.文章总体难度不大,但比较繁重,学习时一定要耐住 ...
- TLD(Tracking-Learning-Detection)学习与源码理解之(六)
TLD(Tracking-Learning-Detection)学习与源码理解之(六) zouxy09@qq.com http://blog.csdn.net/zouxy09 下面是自己在看论文和这些 ...
- TLD(Tracking-Learning-Detection)学习与源码理解之(五)
TLD(Tracking-Learning-Detection)学习与源码理解之(五) zouxy09@qq.com http://blog.csdn.net/zouxy09 下面是自己在看论文和 ...
- TLD(Tracking-Learning-Detection)学习与源码理解之(四)
TLD(Tracking-Learning-Detection)学习与源码理解之(四) zouxy09@qq.com http://blog.csdn.net/zouxy09 下面是自己在看论文和这些 ...
最新文章
- Java多线程编程的常见陷阱
- C/C++笔试、面试题
- Python: 编程遇到的一些问题以及网上解决办法?
- 铝电解电容总结[转]
- 查看sqlserver信息
- 【JZOJ 5421】【NOIP2017提高A组集训10.25】嘟嘟噜
- I2P和TOR 有趣网络精灵
- oa项目环境搭建的操作步骤详解
- 小米手环无法模拟门卡_小米手环3门禁卡设置方法 居然可以模拟门禁卡
- 查看进程及结束进程命令
- 穿越火线怎么在服务器显示人数,现在穿越火线每天平均有多少人在线?
- 条码软件为什么要有等级要求(A级条码)
- Redist过期策略、应用、持久化
- 【Java】棋盘覆盖问题
- 游戏性的根本——浅谈游戏关卡设计
- php数字转人民币,php人民币数字转大写数字的简单示例
- 《Access 2007开发指南(修订版)》一一1.9 Access 2007的新功能
- 刷脸支付基于人脸识别系统的新支付方式
- 中文的游戏配音一定不好吗?
- Spring的事务处理
热门文章
- 从零开始进行Adadelta的梯度下降
- Pyinstaller打包过程中报错“AttributeError: 'str' object has no attribute 'items''”问题解决
- c++ 协程_深入理解异步I/O+epoll+协程
- 合肥师范学院计算机操作系统期末考试题,2005级操作系统期末试卷A卷及答案
- 基于SSM的NBA篮球球队运营系统
- thymeleaf使用
- javascript 弹出层(警告框)的制作(css元素居中、javascript元素居中)
- Logstash 日志搜集处理框架 安装配置
- 阶段5 3.微服务项目【学成在线】_day01 搭建环境 CMS服务端开发_13-MongoDb入门-数据库和集合...
- 阶段3 2.Spring_01.Spring框架简介_05.spring的优势