本文简单介绍了 ArrayList,并对扩容,添加,删除操作的源代码做分析。能力有限,欢迎指正。

ArrayList是什么?

ArrayList 就是数组列表,主要用来装载数据。底层实现是数组 Object[] elementData,当我们装载的是基本数据类型 int, long, boolean, shot...的时候我们只能存储他们对应的包装类型。

与它类似的是 LinkedList,和 LinkedList 相比,它的查找和访问元素的速度较快,但新增,删除的速度较慢。

线程安全吗?

线程不安全。

正常使用场景中,ArrayList 都是用来查询,不会涉及太频繁的增删,如果涉及频繁的增删,可以使用 LinkedList。如果需要线程安全就使用 Vector

VectorArrayList 的线程安全版本,实现方式就是在所有方法加上synchronized,性能较差。

如何扩容?

因为数组的大小是固定,当容量超出了现有数组的大小,就需要进行扩容。

private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 每次扩大原有容量的一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果扩大一半后还是无法满足,则使用minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果超过最大size,则获取最大容量的数组
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}

为什么说ArrayList插入效率低?

原因有两点:

  1. 新增就要检测容量够不够,如果不够就需要扩容
  2. 尾部新增比较快,如果是在数组头部或者中部新增就会慢很多,因为要把后面的元素全部往后移一位
  3. 把元素往后移一位使用的是复制 System.arraycopy(),它是native方法(java定义了接口,其他语言进行实现),所以比较快
/**
* 添加在尾部
*/
public boolean add(E e) {
// 检查容量
ensureCapacityInternal(size + 1);
// 添加在尾部
elementData[size++] = e;
return true;
} /**
* 按指定位置添加
*/
public void add(int index, E element) {
// 检查index
rangeCheckForAdd(index); // 检查容量
ensureCapacityInternal(size + 1);
// index后面的元素全部往后移一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}

删除元素效率如何?

效率和新增差不多,都是要移动元素,但是不需要检查容量和扩容。

public E remove(int index) {
// 检查index
rangeCheck(index); modCount++;
E oldValue = elementData(index); int numMoved = size - index - 1;
if (numMoved > 0)
// index后面的元素全部往前移一位
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work return oldValue;
}

适合做队列吗?

非常不适合。

队列是FIFO,在尾巴进,头部出,出的时候需要移动后面所有数据,效率很低。链表比较适合做队列。

new ArrayList<>(18) 会不会初始化数组大小?

不会初始化数组大小!!!

这是Java Bug。

而且将构造函数与initialCapcity结合使用,然后使用set()方法会抛出异常。

public static void main(String[] args) {
ArrayList<Integer> a = new ArrayList<>(12);
System.out.println(a.size());
a.set(3, 3);
}

总结

  1. 底层实现是数组 Object[] elementData
  2. 查找和访问元素的速度较快,但新增,删除的速度较慢
  3. 线程不安全
  4. 每次扩容原有数组大小的一半

源码系列文章

Java源码系列1——ArrayList

Java源码系列2——HashMap

Java源码系列3——LinkedHashMap

本文首发于我的个人博客 http://chaohang.top

作者张小超

转载请注明出处

欢迎关注我的微信公众号 【超超不会飞】,获取第一时间的更新。

Java源码系列1——ArrayList的更多相关文章

  1. Java源码系列2——HashMap

    HashMap 的源码很多也很复杂,本文只是摘取简单常用的部分代码进行分析.能力有限,欢迎指正. HASH 值的计算 前置知识--位运算 按位异或操作符^:1^1=0, 0^0=0, 1^0=0, 值 ...

  2. Java源码阅读之ArrayList

    基于jdk1.8的ArrayList源码分析. 实现List接口最常见的大概就四种,ArrayList, LinkedList, Vector, Stack实现,今天就着重看一下ArrayList的源 ...

  3. 【java集合框架源码剖析系列】java源码剖析之ArrayList

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本. 本博客将从源码角度带领大家学习关于ArrayList的知识. 一ArrayList类的定义: public class Arr ...

  4. 【Java源码】集合类-ArrayList

    一.类继承关系 public class ArrayList<E> extends AbstractList<E> implements List<E>, Rand ...

  5. Java源码分析之ArrayList

    ArrayList是以数组为基准的容器类,和LinkedList(链表)正好相反.因而ArrayList拥有更好的查找性能,增删操作则差一些.ArrayList封装了对于常规数组的操作,同时可以自动扩 ...

  6. Java源码系列4——HashMap扩容时究竟对链表和红黑树做了什么?

    我们知道 HashMap 的底层是由数组,链表,红黑树组成的,在 HashMap 做扩容操作时,除了把数组容量扩大为原来的两倍外,还会对所有元素重新计算 hash 值,因为长度扩大以后,hash值也随 ...

  7. java源码之集合类ArrayList

    1. ArrayList概述: ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类还提供一些方法来操作内部 ...

  8. java基础解析系列(十)---ArrayList和LinkedList源码及使用分析

    java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...

  9. 深入学习JDK源码系列之、ArrayList

    前言 JDK源码解析系列文章,都是基于JDK8分析的,虽然JDK15马上要出来了,但是JDK8我还不会,我... 类图 实现了RandomAccess接口,可以随机访问 实现了Cloneable接口, ...

随机推荐

  1. Unity事件系统EventSystem简析

    相关组件和类 EventSystem 1.负责InputModule的切换(因为现在游戏大部分都只有一个StanaloneInputModule,所以切换这部分可以先不考虑). 2.负责InputMo ...

  2. SqlServer分页存储过程(多表查询,多条件排序),Repeater控件呈现数据以及分页

        存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出 ...

  3. java小心机(5)| 浅谈类成员初始化顺序

    类成员什么时候会被初始化呢?一般来说:"类的代码在初次使用时才被加载",加载过程包括了初始化. 比如说new A()调用构造函数时,类中全部成员都会被初始化. 但对于static域 ...

  4. Linux系统搭建私有CA证书服务器

    一.CA简介 CA是什么?CA是Certificate Authority的简写,从字面意思翻译过来是凭证管理中心,认证授权.它有点类似我们生活中的身份证颁发机构,这里的CA就相当于生活中颁发身份证的 ...

  5. umake ide -h

    umake ide -husage: umake ide [-h]                 {netbeans,idea,clion,eclipse,atom,idea-ultimate,ec ...

  6. if continue的用法(跳过本次循环,执行下一个循环)

    Python continue 语句跳出本次循环 当需要跳过本次循环的时候,使用continue能跳过本次循环,直接下一个循环 如下脚本: for url in alllink: if url == ...

  7. AI初探

    看东西应该记笔记,不然如过眼云烟,如只逛商场,不买东西,不留下带走什么,就是浪费时间,没有收获,仅此开始,定期梳理看过的东西. 人工智能的目的是什么呢? 答:让机器表现得更像人类,甚至在某些技能上超越 ...

  8. CTF--HTTP服务--路径遍历(拿到www-data用户权限)

    开门见山 1. 扫描靶机ip,发现PCS 172.18.4.20 2. 用nmap扫描靶机开放服务及版本 3. 再扫描靶机的全部信息 4. 用nikto工具探测http服务敏感信息 5. 用dirb工 ...

  9. 机器学习-计算机视觉和卷积网络CNN

    概述 对于计算机视觉的应用现在是非常广泛的,但是它背后的原理其实非常简单,就是将每一个像素的值pixel输入到一个DNN中,然后让这个神经网络去学习这个模型,最后去应用这个模型就可以了.听起来是不是很 ...

  10. C#系列之算数运算符(四)

    今天,我将做一个算术运算符++和--的笔记以及一元运算符和二元运算符同时存在怎么计算的笔记 ++:分为前加加和后加加,但是最后结果都是+1: --:分为前减减和后减减,但是最后结果都是-1: 例如: ...