ArrayList集合(JDK1.8)

简述

List是继承于Collection接口,除了Collection通用的方法以外,扩展了部分只属于List的方法。

常用子类

 ?ArrayList介绍

1.数据结构

其底层的数据结构是数组,数组元素类型为Object类型,即可以存放所有类型数据。

2.源码分析

  2.1类的继承关系

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

说明:ArrayList继承AbstractList抽象父类,实现了List接口(规定了List的操作规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)。

2.2类的属性

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    // 版本号
    private static final long serialVersionUID = 8683452581122892189L;
    // 缺省容量
    private static final int DEFAULT_CAPACITY = 10;
    // 空对象数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 缺省空对象数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    // 元素数组
    transient Object[] elementData;
    // 实际元素大小,默认为0
    private int size;
    // 最大数组容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}

2.3类的构造函数

 public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {       // 初始容量大于0
            this.elementData = new Object[initialCapacity];  // 初始化元素数组
        } else if (initialCapacity == 0) {  // 初始容量为0
            this.elementData = EMPTY_ELEMENTDATA;  //为空对象数组
        } else {  // 初始容量小于0,抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;  // 无参构造函数,设置元素数组为空 
 }

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();   // 转化为数组
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);  // 不为Object数组的话就进行复制
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

说明:当传入的是集合时,会将其转成数组进行处理,并且指定elementData数组的大小,不允许初始化大小小于0,否则抛出异常。

2.4核心方法

 1.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) {
        // 结构性修改加1
        modCount++;
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
private void grow(int minCapacity) {
        int oldCapacity = elementData.length; // 旧容量
        int newCapacity = oldCapacity + (oldCapacity >> 1); // 新容量为旧容量的1.5倍
        if (newCapacity - minCapacity < 0) // 新容量小于参数指定容量,修改新容量
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0) // 新容量大于最大容量
            newCapacity = hugeCapacity(minCapacity); // 指定新容量
        // 拷贝扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
 private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

说明:在这里可以很清楚的看到扩容容量的计算:int newCapacity = oldCapacity + (oldCapacity >> 1),其中oldCapacity是原来的容量大小,oldCapacity >> 1 为位运算的右移操作,右移一位相当于除以2,所以这句代码就等于int newCapacity = oldCapacity + oldCapacity / 2;即容量扩大为原来的1.5倍。,获取newCapacity后再对newCapacity的大小进行判断,如果仍然小于minCapacity,则直接让newCapacity 等于minCapacity,而不再计算1.5倍的扩容。然后还要再进行一步判断,即判断当前新容量是否超过最大的容量 if (newCapacity - MAX_ARRAY_SIZE > 0),如果超过,则调用hugeCapacity方法,传进去的是minCapacity,即新增元素后需要的最小容量,如果minCapacity大于MAX_ARRAY_SIZE,则返回Integer的最大值。否则返回MAX_ARRAY_SIZE。
注意:数组在进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍,操作代价很高,最好在预先元素个数有多少个的情况下指定其容量。
举个栗子

说明:可以看出定义一个无参构造函数时,容器为一个空的数组,并且此时的elementData的值为0

说明:在调用了add函数之后,elementData的值被设置成初始值10,并且将元素1添加进数组中,并且size值加1

举个栗子

说明:当如果一开始定义好了集合的长度,那么在一开始初始化容器的时候,elementData的值就被设置好了

说明:而如果在后边添加的元素大于原先初始化的大小,那么ArrayList的大小会按照1.5倍的来进行动态扩建。如上,原先elementData的值为6,被扩大1.5后变成9

说明:无参构造函数也是相同的道理,如果添加的元素长度大于默认值10,会进行1.5倍进行动态扩建,即elementData的值变成15

 附:size和modCount的区别

 size是ArrayList的变量。modCount是ArrayList的父类AbstractList中的变量,默认值为0。

size记录了ArrayList中元素的数量,modCount记录的是关于元素的数目被修改的次数。modCount在ArrayList的普通操作里可能并没有看出多大用处,但是在涉及到fail-fast就主要是依靠它了。

举个栗子

说明:一开始进行add操作之后,size和modCount的值都是相同的,每次执行都进行加1

说明:当进行remove操作后,容器的元素个数发生改变,所以modCount值会进行加1,而size由于元素减少,进行减1

说明:当再次进行add操作后,size和modCount值同时也会加1

说明:进行set操作进行修改容器元素后,由于元素个数并没有发生改变,所以modCount和size的值都不会发生变化

 2、set函数

 public E set(int index, E element) {
        // 检验索引是否合法
        rangeCheck(index);
        // 旧值
        E oldValue = elementData(index);
        // 赋新值
        elementData[index] = element;
        // 返回旧值
        return oldValue;
    }

说明:设定指定下标索引的元素值。

3、indexOf函数

// 从首开始查找数组里面是否存在指定元素
    public int indexOf(Object o) {
        if (o == null) { // 查找的元素为空
            for (int i = 0; i < size; i++) // 遍历数组,找到第一个为空的元素,返回下标
                if (elementData[i]==null)
                    return i;
        } else { // 查找的元素不为空
            for (int i = 0; i < size; i++) // 遍历数组,找到第一个和指定元素相等的元素,返回下标
                if (o.equals(elementData[i]))
                    return i;
        }
        // 没有找到,返回空
        return -1;
    }

说明:从头开始查找与指定元素相等的元素,注意,是可以查找null元素的,意味着ArrayList中可以存放null元素的。与此函数对应的lastIndexOf,表示从尾部开始查找。

4、get函数

public E get(int index) {
        // 检验索引是否合法
        rangeCheck(index);

        return elementData(index);
    }

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

 E elementData(int index) {
        return (E) elementData[index];
    }

说明:get函数会检查索引值是否合法(只检查是否大于size,而没有检查是否小于0)。在get函数中存在element函数,element函数用于返回具体的元素,返回的值都经过了向下转型(Object -> E)

举个栗子

说明:当index为负值时,程序运行会发生数组越界异常java.lang.ArrayIndexOutOfBoundsException

说明:而当index大于容器中的size值时,则会发生索引越界异常java.lang.IndexOutOfBoundsException

5、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);
        // 赋值为空,有利于进行GC
        elementData[--size] = null;
        // 返回旧值
        return oldValue;
    }

说明:remove函数用户移除指定下标的元素,此时会把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设置为null,这样是为了方便之后将整个数组不被使用时,会被GC,可以作为小的技巧使用。

注明:本人才疏学浅,以上仅为个人日常笔记总结使用

参考资料:【集合框架】JDK1.8源码分析之ArrayList(六)

                  浅谈ArrayList动态扩容

                  <<Thinking In Java(中文版第四版>>

原文地址:https://www.cnblogs.com/liangyueyuan/p/10124158.html

时间: 2024-11-13 06:59:49

ArrayList集合(JDK1.8)的相关文章

JAVA学习第三十五课(常用对象API)- 集合框架(三)—Vector、LinkedList、ArrayList集合演示

集合框架构成图 摘自百度图片 一.Vector集合 演示 虽然Vector已经不常用了,但是还是要了解一下其中的方法 import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Vector

ArrayList 集合的几种遍历的方法

ArrayList 集合  也可称作动态数组(长度可变),在新建的时候是没有默认长度的,在新增数据长度小于10的时候,ArrayList 的长度会自动设置为10 //了解更多可以按住Ctrl 再点击你需要查看的方法或者类名,  查看List源代码,ArrayList源代码 public static void main(String[] args) throws Exception {  Arraylist    新增数据时长度是10   当长度为10的时候 增长是如何增长的?  增长因子数位0

ArrayList集合--关于System.out.print(Object obj);的理解

1.ArrayList集合中常用的方法 ArrayList<Student> stuArrayList = new ArrayList<>(); //定义一个集合对象 stuArrayList.add():    //添加元素 stuArrayList.add(index, e):    //在某个位置添加元素,但不覆盖原元素 stuArrayList.get(index):    //获取某位置的元素 stuArrayList.size():    //获取集合长度 stuArr

ArrayList集合

集合只可以存取引用数据类型,集合的长度可以改变, 基本数据类型对应的引用数据类型 int          Integer double     Double char        Character 定义格式:ArrayList<集合内元素类型> 变量 = new ArrayList<集合内元素类型>(); 方法:变量.add(元素)    增加元素 变量.remove(索引)   删除指定索引的元素 变量.set(索引,元素)  为指定的索引修改元素 变量.get(索引)  

ArrayList集合的语句示例

namespace ArrayList集合的语句示例{    class Program    {        static void Main(string[] args)        {            //一:Stack集合(没有索引)   先进后出,一个一个赋值,一个一个取值,按顺序            //Stack ss = new Stack();            //ss.Push("1");            //ss.Push("2&

ArrayList集合深入学习

ArrayList集合 创建ArrayList ArrayList类集成了AbstractList类 ArrayList中有两个构造方法(方法名相同,方法参数不同叫做重载),默认构造方法通过调用ArrayList(int)来事项ArrayList的创建,传入的值为10 ? ? 因为ArrayList类继承了AbstractList类,其中super调用了父类的默认构造方法,父类的该方法为一个空的方法(只有方法的声明,没有方法的实现),因此这段代码中最关键的是实例化了一个Object的数组,并将此

HashMap集合与ArrayList集合的遍历

ArrayList集合的遍历: HashMap集合的遍历: 随笔说: 在使用迭代器迭代集合的过程中,不能对集合进行增删操作.ArrayList允许重复存放元素, HashMap不支持重复存放元素.

C#语言基础——集合(ArrayList集合)

集合及特殊集合 集合的基本信息: System.Collections 命名空间包含接口和类,这些接口和类定义各种对象(如列表.队列.位数组.哈希表和字典)的集合.System.Collections.Generic 命名空间包含定义泛型集合的接口和类,泛型集合允许用户创建强类型集合,它能提供比非泛型强类型集合更好的类型安全性和性能.ystem.Collections.Specialized 命名空间包含专用的和强类型的集合,例如,链接的列表词典.位向量以及只包含字符串的集合. 常用的集合为Ar

C#的ArrayList集合的使用

集合类是一种将各相同类型的对象集合起来的类,数组实质上也是集合类型中的一种. 集合主要是以线性结构存储结构 C#提供ArrayList类.Queue类.Stack类 1.ArrayList类简介: ArrayList类可以动态地添加和删除元素. ArrayList类相当于一种高级的动态数组,是Array类的升级版本,但它并不等同于数组. 2.与数组的比较: (1)数组的容量是固定的,而ArrayList的容量可以根据需要自动扩充 (2)ArrayList提供添加,删除,插入某一范围元素的方法,但

Java基础知识强化之集合框架笔记27:ArrayList集合练习之去除ArrayList集合中的重复字符串元素

1. 去除ArrayList集合中的重复字符串元素(字符串内容相同) 分析: (1)创建集合对象 (2)添加多个字符串元素(包含重复的) (3)创建新的集合 (4)遍历旧集合,获取得到每一个元素 (5)拿着个元素到新集合中去找,看有没有   有:不搭理它 没有:添加到新集合      (6)遍历新集合 2. 案例代码: 1 package cn.itcast_04; 2 3 import java.util.ArrayList; 4 import java.util.Iterator; 5 6