ArrayList底层代码日记

通过底层代码可以学习到很多东西:

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

由此可见,ArrayList继承自AbastractList,以及实现了以上四个接口;

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>{..}  ==> AbstractCollection<E> implements Collection<E>{..}

List<E> extends Collection<E>{..};

由上引申最后的根源都是Collection;

private static final int DEFAULT_CAPACITY = 10;这是默认的初始化容量;

private static final Object[] EMPTY_ELEMENTDATA = {};

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};以上两个是可以类内部引用的空数组,后者是充当类的默认空值

transient Object[] elementData;  被transient 修饰的变量不会被序列化;

此处注意:该元素不被序列化但是当序列化时元素还是有的;玄机在于ArrayList中的两个方法:

ArrayList在序列化的时候会调用writeObject,直接将size和element写入ObjectOutputStream;反序列化时调用readObject,从ObjectInputStream获取size和element,再恢复到elementData。至于为什么不直接用elementData来序列化,原因在于elementData是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充容量,那么有些空间可能就没有实际存储元素,采用上诉的方式来实现序列化时,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间;

public ArrayList(int initialCapacity) {
  if (initialCapacity > 0) {
    this.elementData = new Object[initialCapacity];
  } else if (initialCapacity == 0) {
    this.elementData = EMPTY_ELEMENTDATA;
  } else {
    throw new IllegalArgumentException("Illegal Capacity: "+
    initialCapacity);
  }

}

这个方法可以定义初始化容量大小的ArrayList;

public ArrayList() {
  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

由此可知:当new一个ArrayList对象时,他是一个默认的空数组;

public void trimToSize() {
  modCount++;
  if (size < elementData.length) {
    elementData = (size == 0)
    ? EMPTY_ELEMENTDATA
    : Arrays.copyOf(elementData, size);
  }
}

此方法可以用来去除动态增长的多余容量,节省空间;ArrayList所说没有用的值并不是null,而是ArrayList每次增长会预申请多一点空间,1.5倍+1,而不是两倍
这样就会出现当size() = 1000的时候,ArrayList已经申请了1200空间的情况;
trimToSize 的作用只是去掉预留元素位置,就是删除多余的200,改为只申请1000,内存紧张的时候会用到;(可自行通过代码调用debug查看元素即知)

public void ensureCapacity(int minCapacity) {
  int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
  // any size if not default element table
  ? 0
  // larger than default for default empty table. It‘s already
  // supposed to be at default size.
  : DEFAULT_CAPACITY;

  if (minCapacity > minExpand) {
    ensureExplicitCapacity(minCapacity);
  }
}

我们在使用Arraylist时,经常要对它进行初始化工作,在使用add()方法增加新的元素时,如果要增加的数据量很大,应该使用ensureCapacity()方法,该方法的作用是预先设置Arraylist的大小,这样可以大大提高初始化速度;总而言之,记住这个函数可以对低层数组扩容就行了,在适当的时机,好好利用这个函数,将会使我们写出来的程序性能得到提升;

至于原因是因为,如果添加元素时第一次没有一次性扩到想要的最大容量的话,它就会在添加元素的过程中,一点一点的进行扩容,要知道对数组扩容是要进行数组拷贝的,这就会浪费大量的时间。如果已经预知容器可能会装多少元素,最好显示的调用ensureCapacity这个方法一次性扩容到位。

public E get(int index) {
  rangeCheck(index);

  return elementData(index);
}

@SuppressWarnings("unchecked")
  E elementData(int index) {
  return (E) elementData[index];
}

由此可见get(i)实质是通过数组的index方式来获取元素;

当ArrayList执行add()方法添加元素时,每次都会去增加modCount;至于这个modCount的用处:

由于ArrayList是非线程安全的,而modCount记录了ArrayList结构性变化的次数;在使用迭代器遍历的时候,用来检查列表中的元素是否发生结构性变化(列表元素数量发生改变)了,主要在多线程环境下需要使用,防止一个线程正在迭代遍历,另一个线程修改了这个列表的结构;

还有:

由此可见,执行add()方法当容量超出了原最大容量,会继续扩容哦;

//TODO

时间: 2024-08-15 15:26:40

ArrayList底层代码日记的相关文章

Java——ArrayList底层源码分析

1.简介 ArrayList 是最常用的 List 实现类,内部是通过数组实现的,它允许对元素进行快速随机访问.数组的缺点是每个元素之间不能有间隔, 当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中. 当从 ArrayList 的中间位置插入或者删除元素时,需要对数组进行复制.移动.代价比较高.因此,它适合随机查找和遍历,不适合插入和删除. 线性表的顺序存储,插入删除元素的时间复杂度为O(n),求表长以及增加元素,取第 i 元素的时间复杂度为O(1). ArrayL

模拟ArrayList底层实现

package chengbaoDemo; import java.util.ArrayList; import java.util.Arrays; import comman.Human; /** * ArrayList 底层实现 */ public class MyArrayList { /** * The value is used for Object Stroage. */ private Object value[]; /** *The size is the number of O

图像的点运算----底层代码与Halcon库函数

最基本的图像分析工具----灰度直方图.使用直方图辅助,可以实现4大灰度变换,包括线性灰度变换(灰度拉伸).灰度对数变换.灰度伽马变换.灰度分段线性变换:使用直方图修正技术,可以实现2大变换,包括直方图均衡化和直方图规定化. 一.灰度直方图 灰度直方图分为一般灰度直方图和归一化灰度直方图,灰度直方图统计图像中各个灰度级出现的次数,而归一化的灰度直方图统计的是各个灰度级出现的频率.因此,灰度直方图通常在定量上,有总体像素个数以及各个灰度像素个数:在定性上,可以看出整体灰度动态范围,即对比度与亮度.

Java底层代码实现单文件读取和写入(解决中文乱码问题)

需求: 将"E:/data/车站一次/阿坝藏族羌族自治州.csv"文件中的内容读取,写入到"E:/data//车站一次.csv". 代码: public class FileOpe { public static void main(String[] args) { sigle(); } public static void sigle(){ BufferedReader bufr = null; BufferedWriter bufw = null; try {

Java底层代码实现多文件读取和写入

需求: "E:/data/"目录下有四个文件夹,如下: 每个文件夹下有几个.csv文件,如下: 将每个文件夹下的.csv文件合并成一个以该文件夹命名的.csv文件. 做法: 找到"E:/data"这个目录,循环读取目录下的四个文件夹: 针对每个文件夹,循环读取目录下的文件列表: 将读取的文件写入要合并的文件中. 代码: public class FileOperation { public static void main(String[] args) { comb

JAVAEE——BOS物流项目02:学习计划、动态添加选项卡、ztree、项目底层代码构建

1 学习计划 1.jQuery easyUI中动态添加选项卡 2.jquery ztree插件使用 n 下载ztree n 基于标准json数据构造ztree n 基于简单json数据构造ztree(重点) 3.数据库建模工具PowerDesigner使用方式 4.myeclipse翻转引擎插件使用(了解) 5.项目底层代码构建(重点) n 持久层代码抽取 n 表现层代码抽取 6.实现BOS项目登录和注销功能 2 jQuery easyUI中动态添加选项卡 l 用于动态添加一个选项卡 l 选中指

ArrayList底层原理

ArrayList底层采用数组实现,访问特别快,它可以根据索引下标快速找到元素.但添加插入删除等写操作效率低,因为涉及到内存数据复制转移. ArrayList对象初始化时,无参数构造器默认容量为10,当空间不足时会扩容,扩容后的容量是老容量的1.5倍.Java8的ArrayList源代码第259行,可以看到将原始容量数右移一位,即每次扩充老容量的二分之一,即新增0.5倍,换句话说新容量是老容量的1.5倍. 原文地址:https://www.cnblogs.com/huigee/p/9725256

C#中添加对象到ArrayList的代码

把开发过程中比较好的一些代码段做个备份,下面代码是关于C#中添加对象到ArrayList的代码. ArrayList alcollect = new ArrayList();string str = "learn csharp";alcollect.Add(str);alcollect.Add("hello world");alcollect.Add(500);alcollect.Add(new object()); AddRange方法支持添加一个范围内的对象.

手撕ArrayList底层,透彻分析源码

ArrayList概述 Hello大家好,今天就来介绍一下ArrayList,说到ArrayList,很多人都知道它的底层是使用数组实现的,线程不安全的,说到它的特点,都会说查找快,增删慢,因为面试题大家都是这么背过来的.今天就来说说它的底层源码吧. ArrayList更准确的说是动态数组去实现的,这里使用动态两字,是为了能够充分体现它的特点. 再者就是ArrayList不是线程安全的,所以效率比较高,但是否这个是绝对的呢?答案是否定的 . ArrayList底层源码 public class