秋招之路1:ArrayList的底层实现原理

ArrayList

概述

  1. ArrayList 是基于数组实现的,是一个动态数组
  2. ArrayList 不是线程安全的,只能在单线程环境下;多线程使用ArrayList,应该考虑Collections.synchronizedList(List l)和concurrent并发包下的CopyOnWriteArrayList类
  3. ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输;
    实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问;
    实现了Cloneable接口,能被克隆。

首先,我的length和size有点混

1.length属性是针对Java中的数组来说的,要求数组的长度可以用其length属性;
2.length()方法是针对字符串来说的,要求一个字符串的长度就要用到它的length()方法;
3.java中的size()方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!

elementData

transient Object[] elementData:
transient:用transient关键字标记的成员变量不参与序列化过程。

size

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial        这个是什么意思呢?说明一个序列化属性
     */
    private int size;

构造方法

一共三种:
//指定初始大小的构造方法
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;
    }     

//使用集合
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();    //转换为数组,这个数组中的类型是根据c判断的,有可能不是Object[]
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)    //不是Oject[]时候,利用copyOf进行转换
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

常常出现的modCount,expectedModCount变量

这一变量常常和线程安全有关,是记录修改次数的两个变量

add方法

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
//调用下面的这个方法
private void ensureCapacityInternal(int minCapacity) {
        //用来判定是否是初次增加。
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
//调用下面这个方法
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
//看看本次增加的值,是否大于elementData了,大于再增加
            grow(minCapacity);
    }
//调用下面的这个方法
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
//每次成1.5倍的增长
        if (newCapacity - minCapacity < 0)
//1.5倍都不够,就直接用minCapacity作为新容量
            newCapacity = minCapacity;
//是否超过MAX_ARRAY_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);
    }

add方法的另外一个重载

public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

//作用就是: 如果当前位置有元素,则向右移动当前位于该位置的元素以及所有后续元素(将其索引加1)。

主要涉及这个方法
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
代码解释:
  Object src : 原数组
int srcPos : 从元数据的起始位置开始
  Object dest : 目标数组
  int destPos : 目标数组的开始起始位置
  int length : 要copy的数组的长度

add的另一个重载

//从指定的位置开始,将指定collection中的所有元素插入到此列表中。
public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

get方法

public E get(int index) {
        rangeCheck(index);
//检查是否越界
        return elementData(index);
//抑制返回值警告的一个方法
    }

set方法

// 用指定的元素替代此列表中指定位置上的元素,并返回以前位于该位置上的元素。
public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

remove

有三种实现

//移除制定位置上的元素
public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        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

        return oldValue;
    }

//移除指定元素:
remove(Object o)中通过遍历element寻找是否存在传入对象,一旦找到就调用fastRemove移除对象。
fastRemove:基本是和remove(int index)一致,但是跳过了边界检查条件。
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) { //java 判断null的方法。
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) { //java的.equals是判断是否是值相等
fastRemove(index);
return true;
}
}
return false;
}
//removeRange
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);

    // clear to let GC do its work    //后面的null元素,让gc去回收。
    int newSize = size - (toIndex-fromIndex);
    for (int i = newSize; i < size; i++) {
        elementData[i] = null;
    }
    size = newSize;
}

toArray方法

public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
//这个toArray方法,主要是用于array转换成特定的比如是String[]等数组时候用的
//链接门[https://www.cnblogs.com/goloving/p/7693388.html]
public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

Fail-Fast机制:

ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。
具体参见HashMap的实现原理 中的Fail-Fast机制。

原文地址:https://www.cnblogs.com/whyaza/p/12318346.html

时间: 2024-11-02 11:10:49

秋招之路1:ArrayList的底层实现原理的相关文章

秋招之路2: implements 与 extends ,抽象类总结

extends[继承类]的特点 子类拥有父类的非private属性,方法. 子类可以拥有自己的属性和方法,子类可以对父类进行扩展. 子类可以用自己的方式实现父类的方法. 减少了代码的冗余,即相同重复的代码可以放在父类里面,各自子类有不同的实现. 相关关键字 super:引用父类成员变量.局部变量.方法. this:引用本类成员变量,方法. 需要注意的是:类的成员变量与局部变量重名,类的成员变量将被隐藏,如果要使用类的成员变量,需要使用this引用之. implements[实现接口]的特点 1.

秋招之路7:全面JVM

jvm全景图 其中蓝色区域是线程独有的,黄色区域是线程共享的 分析方法 javap 命令 javap -c 可以对代码进行反汇编 里面的各种指令,可以用jvm指令手册一个一个查 一个线程的全景图 程序计数器:和计组一样,是存放下一条指令所在单元的地址的地方. 栈帧:一个方法对应一块栈帧内存区域.放自己方法里面各种变量用的. 其中局部变量表一般是放局部变量.操作数栈,一般是放操作过程中的常量. 局部变量表中的对象,存的是对象的地址,对象数据存在堆空间中. 动态链接:将符号引用转换为直接引用. 本地

秋招之路8:JAVA锁体系和AQS抽象队列同步器

整个的体系图 悲观锁,乐观锁 是一个广义概念:体现的是看待线程同步的不同角度. 悲观锁 认为在自己使用数据的时候一定有别的线程来修改数据,在获取数据的时候会先加锁,确保数据不被别的线程修改. 实现:关键字synchronized,接口Lock的实现类 适用场景:写操作多,先加锁可以保证写操作时的数据正确. 乐观锁 认为自己在使用数据的时候不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据. 实现:CAS算法,例如AtomicInteger类的原子自

秋招之路9:juc并发

j.u.c是java.util.concurrent的简称 通过查api,了解到是由以下这三个组成的. juc包图 锁的两种实现方式 java并发编程,关于锁的实现方式有两种: 1.基于synchronized关键字实现同步,jvm内置锁,也叫隐式锁,由jvm自动加锁和解锁 2.juc下的lock接口实现的更加灵活的锁的实现方式,也叫显示锁,需要手动加锁和解锁 重要分类 locks部分:显示锁(互斥锁和读写锁)相关: atomic部分:原子变量类相关,是构建非阻塞队列算法的基础,使用CAS实现:

[转载]从春招到秋招,一个本科生的求职之路。

原文:从春招到秋招,一个本科生的求职之路. 自报家门,北理工软件学院本科生. 主要部分: 1.毕业去向选择 2.春招过程 3.暑期实习 4.秋招辛酸路程 5.一点感悟 1.毕业去向选择问题 从大一开始,就决定毕业找工作,方向是有了,但是三年多过去了,到现在才发现,大学期间并没有为这个方向做出太大的努力,这也成为我一个本科生找工作的很大障碍,实践能力严重不足.我的情况是:我决定工作,但是由于高中的思维,太看重学习成绩,成绩搞的很好,虽然有保研资格,但是又不读研,找工作的时候,好的互联网公司不看成绩

[找工作] 2019秋招|从春招到秋招,Java岗经验总结(收获AT)

转自(有更多) https://blog.csdn.net/zj15527620802/article/month/2018/10 前言 找工作是一件辛酸而又难忘的历程.经历过焦虑.等待.希望,我们最终都能去到属于自己理想的地方. 从春招到秋招,我总计面试了数十家公司.春招面试了腾讯.去哪儿.360.小米.携程.华为.今日头条.美团.菜鸟网络等9家公司.除了今日头条止步三面.菜鸟物流备胎至七月底才回绝,其他公司均通过了面试.最后在美团和腾讯中纠结了一段时间后,选择了腾讯去实习.秋招腾讯转正的还算

网易2017秋招编程题集合-牛客网

网易2017秋招编程题集合-牛客网 链接:https://www.nowcoder.com/questionTerminal/0147cbd790724bc9ae0b779aaf7c5b50来源:牛客网 如果一个数字序列逆置之后跟原序列是一样的就称这样的数字序列为回文序列.例如: {1, 2, 1}, {15, 78, 78, 15} , {112} 是回文序列, {1, 2, 2}, {15, 78, 87, 51} ,{112, 2, 11} 不是回文序列. 现在给出一个数字序列,允许使用一

错过了春招的你,难道还不为秋招做准备吗

序 如今的校招,像一场突围赛,很多人"牺牲"在前进的路上,那些突围成功的人,不是因为他们刀枪不入,而是他们有扎实的准备,周密的计划以及可圈可点的技巧. 获得校招的成功,大致需要经历计划.准备.应聘这三个过程,文中全面覆盖这些内容. 无论你是刚刚进入大学的萌新,还是身处在大二.大三的任何阶段,这篇文章都尽可能给出一些建议和指导. 目录 简单的自我介绍 [计划]时间安排:对找工作的同学十分重要的时间点,以及如何安排自己的行程 [准备]技能清单:需要具备的能力 [准备]如何提高:如何有针对性

错过了春招,难道你还不为秋招做准备吗

序 如今的校招,像一场突围赛,很多人"牺牲"在前进的路上,那些突围成功的人,不是因为他们刀枪不入,而是他们有扎实的准备,周密的计划以及可圈可点的技巧. 获得校招的成功,大致需要经历计划.准备.应聘这三个过程,文中全面覆盖这些内容. 无论你是刚刚进入大学的萌新,还是身处在大二.大三的任何阶段,这篇文章都尽可能给出一些建议和指导. 目录 简单的自我介绍 [计划]时间安排:对找工作的同学十分重要的时间点,以及如何安排自己的行程 [准备]技能清单:需要具备的能力 [准备]如何提高:如何有针对性