一、基础简介

1、ArrayList继承关系

2、底层用数组来存储数据,数据会在ArrayList创建的时候一并初始化。如果创建ArrayList的时候,没有设置容量,则会delay到第一次add数据时初始化这个数组。

3、默认的初始容量是10

4、System.arrayCopy()

ArrayList严重依赖System.arrayCopy()方法,不管是Android还是JDK,这个方法都是native的,定义如下:

二、几个重要的方法

1、add(E e)

如上代码所示:在添加数据时,首先扩容至size+1,也就是当前元素个数+1。如果数组还没被初始化,则用默认容量和size+1取最大,然后初始化,这个操作最多执行一次。接着判断,如果size+1大于当前数组的容量,才会真正扩容。具体扩容的策略是:首先在当前数组长度基础上乘以1.5倍,然后与size+1取最大值使用,最后与数组可接受的最大容量值MAX_ARRAY_SIZE比较,如果比MAX_ARRAY_SIZE还大,则设置一个hugeCapacity。

梳理一下,使用默认方式创建ArrayList,此时elementData数组尚未被初始化。添加第一个元素,初始化elementData数组,length=10。size表示的是数组中存储元素的个数,size+1和10比较,发现数组容量足够,则不会触发扩容。继续添加直到第11个元素,size+1=11,11>10,触发扩容逻辑。10*1.5=15,大于我们需要的最小容量11,所以按照15扩容即可。扩容操作调用了Arrays.copyOf()方法,说白了,就是创建一个新的数组,容量是扩容之后的容量。然后将旧数组中的值全部拷贝到新数组中。

add方法的最后,就是将数据放到以size为下标的数组位置,然后size+1。所以size指的永远是数组中的实际元素个数,这个值恰好能作为数组中最后一个元素之后的下标位置。

2、add(int index, E e)

先扩容,然后将elementData数组中,index位置及其之后所有的元素拷贝到index+1位置及其之后对应的位置。说白了,就是index及其之后的所有元素全部后移一位,腾出index位置的空间,给要插入的元素。这里的性能较差,所以如果有大量的中间位置插入操作,不建议使用ArrayList。

3、addAll(Collection c)

将c列表中的数据追加到elementData之后,首先扩容,然后将a数组所有元素拷贝到elementData数组下标从size开始的位置,最后size+=numNew;

4、addAll(int index,Collection c)

原理和之前的差不多。

5、remove(int index)

代码比较简单,就是将index之后的元素全部前移一位,这样自然会占用index所在位置,相当于删除了index位置的元素。要注意的是,前移之后,数组最后一位元素还是存在的,也就是说旧数组中最后一个元素存在了2份:最后一个位置和倒数第二个位置都有。所以这里需要手动删除最后一个位置的元素。

5、remove(Object obj)

原理都是类似的,只不过由于不再是下标,所以需要挨个对比,对比的标准就是:如果obj为null,直接判断引用是否是同一个,否则用equals方法。

6、私有的batchRemove(Collection c)

removeAll()和retainAll()方法都用到了batchRemove()方法,如下:

有两种选择,取决于complement的参数,如果为true,则保留本集合和c集合中都有的元素,相当于取交集;如果为false,在本集合中删除c集合中有的元素;

7、get(int index)

get方法比较简单,判断下标是否越界,如果没有越界,直接返回下标对应数组位置的元素即可;

8、set(int index, E e)

set方法的作用是替换指定位置的元素,所以代价较低。

9、subList(int fromIndex, int toIndex)

subList()方法会返回一个新的SubList对象,如注释中所言,可以认为返回的List只是原List的一个视图,SubList类同样继承自AbstractList,所以ArrayList该有的一些操作它都有。要注意的是:所有对subList的操作都会映射到原来的List中,如上图,this.parent就是ArrayList原对象。

几个问题要注意:

1、subList生成子列表后,就不要去操作原列表了,负责会抛出ConcurrentModificationException;

2、为什么这么设计?新创建一个独立的ArrayList,然后将范围内的元素直接添加或者clone一份到新ArrayList返回不行么?可能的考虑是,如果要处理当前列表中某一范围内的数据时,这种方式可以提供更加优雅的实现。

重点参考:http://wiki.jikexueyuan.com/project/java-enhancement/java-thirtyseven.html

10、forEach()

11、iterator()

返回Itr对象,Itr类继承自Iterator,实现了hasNext()、next()等方法,能允许顺序遍历列表中的所有元素;

12、listIterator()

这两个方法返回一个ListItr对象,ListItr类实现了几个特殊的方法,能允许我们倒序遍历列表,但是倒序开始的位置需要我们来设置。如果使用默认的listIterator()方法,相当于从0处倒序遍历,一个元素都没有;

如果调用list.listIterator(list.size())方法,可以倒序遍历整个列表。

13、contains(Object obj)和indexOf(Object obj)

逻辑简单,不用分析了。

【Java集合系列一】ArrayList解析的更多相关文章

  1. Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  2. java集合系列之ArrayList源码分析

    java集合系列之ArrayList源码分析(基于jdk1.8) ArrayList简介 ArrayList时List接口的一个非常重要的实现子类,它的底层是通过动态数组实现的,因此它具备查询速度快, ...

  3. java集合系列之三(ArrayList)

    上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解ArrayLi ...

  4. 【Java集合系列】---ArrayList

    开篇前言--ArrayList中的基本方法 前面的博文中,小编主要简单介绍java集合的总体架构,在接下来的博文中,小编将详细介绍里面的各个类,通过demo.对比,来对java集合类进行更加深入的理解 ...

  5. Java集合系列[1]----ArrayList源码分析

    本篇分析ArrayList的源码,在分析之前先跟大家谈一谈数组.数组可能是我们最早接触到的数据结构之一,它是在内存中划分出一块连续的地址空间用来进行元素的存储,由于它直接操作内存,所以数组的性能要比集 ...

  6. Java 集合系列 09 HashMap详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  7. Java 集合系列 10 Hashtable详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  8. Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和性能分析)

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  9. Java 集合系列 06 Stack详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  10. Java 集合系列 05 Vector详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

随机推荐

  1. keepalived 工作原理

    keepalived主要通过vrrp协议为基础进行通信 所以先从VRRP协议说起: VRRP: 英文全称 Virtual Router Redundancy Protocol, .中文:虚拟路由冗余协 ...

  2. shell 三剑客

    grep 过滤来自一个文件或标准输入匹配模式内容. 除了grep外,还有egrep.fgrep.egrep是grep的扩展,相当于grep -E.fgrep相当于grep -f,用的少. Usage: ...

  3. Linux 配置SSH 无密钥登陆

    根据SSH 协议,每次登陆必须输入密码,比较麻烦,SSH还提供了公钥登陆,可以省去输入密码的步骤. 公钥登陆:用户将自己的公钥存储在远程主机上,登陆的时候,远程主机会向用户发送一串随机字符串,用户用自 ...

  4. 『Numpy』np.meshgrid

    生成网格坐标,一个很好的说明图如下: 虽然xy双坐标比较常用,但实际上其输入可以是任意多的数组,输出数组数目等于输出数组数目,且彼此间shape一致. 如果输入数组不是一维的,会拉伸为1维进行计算. ...

  5. CRT/LCD/VGA Information and Timing

    彩色阴极射线管的剖面图: 1. 电子QIANG Three Electron guns (for red, green, and blue phosphor dots)2. 电子束 Electron ...

  6. Vue基础进阶 之 过渡效果

    进入/离开过渡效果:Vue在插入.更新或移除DOM时,可以设置一些动画效果: 如何使用过渡效果:利用<transition></transition>组件将需要应用的过渡效果的 ...

  7. keepalived + nginx 搭建负载均衡集群

    第一章 keepalived 1.1 keepalived 服务说明 Keepalived软件起初是专为LVS负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可以实现 ...

  8. Shell 脚本格式注意事项

    if 条件判断格式 if [ ! -f file.txt ];then cmd else cmd fi 注1:! 代表非.不存在文件就成功. 注2:再有参数 变量 需要 [] 阔起 1 运算书写写格式 ...

  9. 利用JS打印质数

    我爱撸码,撸码使我感到快乐!大家好,我是Counter,今天非常愉快,没有前几天的相对比较复杂的逻辑思维在里面,今天来写写,利用JS打印质数,基本上很多面试,会很经常的考到.那废话不多说,直接上代码: ...

  10. JS(JavaScript)的初了解3(更新中···)

    1. {} 在JS中我们把它叫代码块.如果代码块里的内容没有执行完,语句不会向下执行. 代码块是一个独立的整体.如果JS中某一条语句出错,那么就会在此终止不会向下执行. 2. 循环语句 循环,就是对一 ...