Java ArrayList源码分析(有助于理解数据结构)

arraylist源码分析

1.数组介绍

数组是数据结构中很基本的结构,很多编程语言都内置数组,类似于数据结构中的线性表

在java中当创建数组时会在内存中划分出一块连续的内存,然后当有数据进入的时候会将数据按顺序的存储在这块连续的内存中。当需要读取数组中的数据时,需要提供数组中的索引,然后数组根据索引将内

存中的数据取出来,返回给读取程序。在Java中并不是所有的数据都能存储到数组中,只有相同类型的数据才可以一起存储到数组中。

  

因为数组在存储数据时是按顺序存储的,存储数据的内存也是连续的,所以他的特点就是:寻址读取数据比较容易,插入和删除比较困难。

带参构造方法

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);} //如果既不大于零,也不小于零,就会报错抛出异常
    }

空参构造方法

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //给一个成员变量赋值
    }  private static final Object[] DEFAULTCAPACITY EMPTY ELEMENTDATA={}; //空参构造方法创建的数组就是一个空的

下面这个用的不多,能看懂即可

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray(); //把Collection这个集合也变成了一个数组,传给了当前数组elementData     //大于0,就拷贝数组
        if ((size = elementData.length) != 0) { //判断当前elementData数组的长度,如果不等于0
            if (elementData.getClass() != Object[].class) //判断类型是不是Object数组类型,如果不是这个类型
                elementData = Arrays.copyOf(elementData, size, Object[].class);  //拷贝一个数组,然后长度,然后把Object[].class数组传进去,然后生成一个elementData数组     //不是大于0,就拷贝一个空的
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA; //设置为空数组
        }
    }

 添加元素

public boolean add(E e) {    //检测是否需要扩容
       下¹ensureCapacityInternal(minCapacity:size + 1);  // size是当前存的数据个数    //数组赋值
        elementData[size++] = e;
        return true;
    }

 扩容的入口方法,计算容量

private void 上¹ensureCapacityInternal(int minCapacity){    //minCapacity最小值,传的时候就是+1后的
下下³ensureExplicitCapacity(下²calculateCapacity(elementData,minCapacity)); //计算容量,传入一个数组elementData,再传一个最小容量minCapacity

计算最小容量

private static int 上²calculateCapacity(Object[] elementData, int minCapacity){
//如果elementData是空的
if(elementData==DEFAUL TCAPACITY EMPTY EL EMENTDAT)|
//返回一个默认或者minCapacity,最后是返回其中最大的
return Math. max(DEFAULT_CAPACIT minCapacity);
//不是空,就返回size+1的值
return minCapacity;
}
private void 上上³ensureExplicitCapacity(int minCapacityp{//只要发生修改就+1
modCount++; //是否满足扩容的条件   最小容量(当前数组存值得容量?) - object数组的长度if(minCapacity-elementData. length〉0)//elementData.lenth=10 (默认容量为10)
下¹grow(minCapacity);

modCount++ : 该字段表示list结构上被修改的次数。在对集合进行新增或移除操作时会使modCount+1,这是一个记录集合变化次数的变量。

作用是在使用迭代器Iterator对集合进行遍历时,用modCount来判断集合内数据没有发生变化,否则会抛出异常。

 数组扩容方法

private void 上¹grow(int minCapacity){
   //计算当前数组容量,也就是老的容量   int oldCapacity=elementData. length;   //新的数组容量 = 老容量 + 老容量/2  (老容量的1.5倍)   int newCapacity=oldCapacity+(oldCapacity >>1);  >>1 除二

     // oldcapacity=10
    // oldcapacity >>1
    // 0000 1010 >> 1
    // 0000 0101 = 5

    //判断新的数组容量-之前最小容量

   if(newCapacity-minCapacity<0)
      newCapacity=minCapacity;   if (newCapacity - MAX_ARRAY_SIZE>0)  //MAX_ARRAY_SIZE:值特别大,如果新的数组比它还大,就越界了,或者直接返回最大长度
      newCapacity = hugeCapacity(minCapacity);   elementData=Arrays. copyof(elementData, newCapacity);
    public void add(int index, E element) {     //判断下标是否越界
        下1rangeCheckForAdd(index);
     //检测是否需要扩容
        ensureCapacityInternal(size + 1);      //把index之后的所有元素向后移动一位
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);     //将index位置覆盖上新值
        elementData[index] = element;
        size++;
    }
private void 上1rangeCheckForAdd(int index){
if(index>size || index<0)
throw new IndexOutofBoundsException(outofBoundsMsg(index));

System.arraycopy(elementData, index, elementData, index + 1,size - index); 举例

int[] elementData = new int[10];
 for(int i=0;i<5;i++){ //数组的下标 0,1,2,3,4
elementData[i]=i+1;  //数组的值 1,2,3,4,5
System. out. println("=="+Arrays. toString(elementData));
int size=5;
int index=1; //插入数组下标位置
 System. arraycopy(elementData, index, elementData, destPos: index+1, length: size-index);   原数组,插入指定的位置,又指定的数组,指定的位置为当前位置+1,到5-1=4的位置 >>从插入位置往后算
System. out. println("--"+Arrays. toString(elementData));

打印结果

==  [1,2,3,4,5,0,0,0,0,0]
 一  [1,2,2,3,4,5,0,0,0,0]  2:是要插入的数值

 

删除方法

指定位置删除

public E remove(int index) {     检测当前下标是否越界
        下1rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);
     将index+1及之后的元素向前移动一位,覆盖被删除的值
        int numMoved = size - index - 1;
        if (numMoved > 0)
            下下2System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);     将最后一个位置的元素清空
        elementData[--size] = null; 

        return oldValue;
    }
private void 上1rangeCheck(int index){
if(index >=size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

上上2 System.arraycopy(elementData, index+1, elementData, index, numMoved); 举例int[] elementData = new int[10];

for(int i=0;i<10;i++){ //数组的下标 0,1,2,3,4,5,6,7,8,9
elementData[i]=i+1;  //数组的值 1,2,3,4,5,6,7,8,9,10}int size = elementData.lengh;  //数组里有几个数,长度就是几    elementData.lengh = 10int index = 2;int numMoved = size - index - 1;
System. out. println("==" + Arrays. toString(elementData));
System. arraycopy(elementData, srcPos: index+1, elementData, index, numMoved);   原数组,指定的位置为要删除的下标往后一个位置,又指定的数组,删除位置的下标,10-2-1=7  >>从指定删除下标位置往后数,全部向前移动一位
System. out. println("--" + Arrays. toString(elementData));

打印结果
 ==  [1,2,3,4,5,6,7,8,9,10]   3:要删除的数字
 一  [1,2,4,5,6,7,8,9,10,10]  

指定元素删除

public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    下1fastRemove(index);
                    return true;} //从0开始遍历,大小为数组长度,找到后直接删除
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }     如果没有匹配元素,返回false
        return false;
    }

快速删除, 没有检测index下标的原因:因为之前需要指定下标删除,现在是直接指定删除某个元素,如果元素不存在,for语句遍历也不会找到这个元素,所有,只要能满足两个if语句其中一个,那这个元素一定在我的合理范围内的,所以就不需要检测有没有下标

快速删除,没有检测index下标

private void 上1fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

原文地址:https://www.cnblogs.com/xiaozhongfeixiang/p/11514674.html

时间: 2024-07-30 05:19:36

Java ArrayList源码分析(有助于理解数据结构)的相关文章

Java - ArrayList源码分析

java提高篇(二一)-----ArrayList 一.ArrayList概述 ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小. 每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小.默认初始容量为10.随着ArrayList中元素的增加,它的容量也会不断的自动增长.在每次添加新的元素时,Array

JAVA Collection 源码分析(一)之ArrayList

到今天为止,差不多已经工作一年了,一直在做的是javaweb开发,一直用的是ssh(sh)别人写好的框架,总感觉自己现在高不成低不就的,所以就像看看java的源码,顺便学习一下大牛的思想和架构,read and write一直是提高自己编程水平的不二法门,写博客只是记录自己的学习历程,方便回顾,写的不好的地方,请多多包含,不喜勿喷,好了废话少说,现在让我们开始我们的历程把,Let's go!!!!!!!! 想看源码无从下手,不知道有没有跟我一样感觉的人们,今天用Intellij发现了可以找出类与

Java笔记---ArrayList源码分析

一.前言 一直就想看看java的源码,学习一下大牛的编程.这次下狠心花了几个晚上的时间,终于仔细分析了下 ArrayList 的源码(PS:谁说的一个晚上可以看完的?太瞎扯了).现在记录一下所得. 二.ArrayList 源码分析 2.1 如何分析? 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 Eclipse 找到所需要分析的类(此处就是 ArrayList) 新建类:新建一个类,命名为 ArrayList,将源码拷贝到该类.因为我

Java集合系列之ArrayList源码分析

一.ArrayList简介 ArrayList是可以动态增长和缩减的索引序列,它是基于数组实现的List类. 该类封装了一个动态再分配的Object[]数组,每一个类对象都有一个capacity属性,表示它们所封装的Object[]数组的长度,当向ArrayList中添加元素时,该属性值会自动增加.如果想ArrayList中添加大量元素,可使用ensureCapacity方法一次性增加capacity,可以减少增加重分配的次数提高性能. ArrayList的用法和Vector向类似,但是Vect

Java集合源码分析(二)ArrayList

ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类. ArrayList实现了Serializable接口,因此它支持序列化,能够通过

Java I/O系列(二)ByteArrayInputStream源码分析及理解

定义 继承了InputStream,数据源是内置的byte数组buf,那read ()方法的使命(读取一个个字节出来),在ByteArrayInputStream就是简单的通过定向的取buf元素实现的 核心源码理解 源码: 1 public ByteArrayInputStream(byte buf[], int offset, int length) { 2 this.buf = buf; 3 this.pos = offset; 4 this.count = Math.min(offset

JAVA Collection 源码分析(二)之SubList

昨天我们分析了ArrayList的源码,我们可以看到,在其中还有一个类,名为SubList,其继承了AbstractList. // AbstractList类型的引用,所有继承了AbstractList都可以传进来 private final AbstractList<E> parent; // 这个是其实就是parent的偏移量,从parent中的第几个元素开始的 private final int parentOffset; private final int offset; int s

ArrayList源码分析--jdk1.8

ArrayList概述   1. ArrayList是可以动态扩容和动态删除冗余容量的索引序列,基于数组实现的集合.  2. ArrayList支持随机访问.克隆.序列化,元素有序且可以重复.  3. ArrayList初始默认长度10,使用Object[]存储各种数据类型. ArrayList数据结构   数据结构是集合的精华所在,数据结构往往也限制了集合的作用和侧重点,了解各种数据结构是我们分析源码的必经之路.  ArrayList的数据结构如下: ArrayList源码分析 /* * 用数

Java 集合源码分析(一)HashMap

目录 Java 集合源码分析(一)HashMap 1. 概要 2. JDK 7 的 HashMap 3. JDK 1.8 的 HashMap 4. Hashtable 5. JDK 1.7 的 ConcurrentHashMap 6. JDK 1.8 的 ConcurrentHashMap 7. 最后补充一下 HashMap 中的一些属性和方法 附:更这个系列感觉自己像是又挖了一个坑??,不过趁自己刚好工作不太忙,有空闲期,静下心来研究学习源码也是一件很值得做的事,自己尽量会把这个坑填完??.