arraylist函数及相关解释(代码片段)

天堂1223 天堂1223     2022-12-05     589

关键词:

Java中ArrayList是使用对象数组实现的,默认的数组容量为10。在ArrayList中,默认创建了两个空的对象数组,一个用户该对象的空实例,一个用于当第一个元素被添加的时候,该数组要被填充多少。

同时,该类中还有一个transient属性的对象数组,transient描述的对象属性,表示在序列化的时候,不用去序列化该属性。

1、ArrayList构造函数

首先我们看一下ArrayList的构造函数,该类有三个构造函数,分别是:

  1. ArrayList(int initialCapacity)
  2. ArrayList()
  3. ArrayList(Collection<? extends E> c)

第一个构造函数,指定了ArrayList的初始化大小。我们看一下该函数的函数体:

 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);
        
    

在该构造函数中,判断初始化大小的值,如果大于零,则直接新建一个数组长度为initialCapacity的对象数组,并赋值给elementData。如果等于0,则elementData直接被赋值给默认创建的空对象数组。如果小于0,则抛出异常。

第二个构造函数为空参数,该函数中直接创建一个空的对象数组,并且其初始化大小为默认的大小。

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);
         else 
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        
    

首先,调用集合的toArray()方法,将集合转换为对象数组,并赋值给elementData,接着给ArrayList中描述列表长度的属性size赋值为数组的长度,如果数组长度不为0,在这里,因为toArray()函数可能不会返回指定对象类型的数组,所以需要调用Arrays.copyOf()函数,该函数可以指定赋值的对象的类型;如果数组长度为0,则将elementData直接赋值为初始创建的空数组。

2、ArrayList中的功能函数

2.1 trimToSize()函数

该函数是将ArrayList实例的容量修建为列表实际的大小。

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

所有在ArrayLisy函数操作的地方,都会增加modCount的值,这样是为了在后续的函数中判断是否出现ConcurrentModificationException的异常。

只有在当前列表实际的长度小于数组的长度的时候,才会执行该操作。所以在该函数中进行了判断。如果实际大小为0,则将elementData直接赋值为空数组,否则,使用Arrays.copyOf()函数,对数组的实际长度进行复制。

2.2 ensureCapacity(int minCapacity)函数

通过参数指定的最小容器来确保列表的容量,以使得列表能够容纳至少指定容量大小的元素。

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);
        
    

这里比较有意思的是判断elementData是否是默认元素表,如果不是默认元素表,则最小扩展值设置为0;如果是默认元素表,则最少扩展值为默认容量。这样对参数中指定的最小容量与当前扩展值做对比,如果最小容量较大,则进行扩容处理。便会调用ensureExplicitCapacity(int minCapacity)函数。

2.3 ensureExplicitCapacity(int minCapacity)函数

该函数就不会去进行刚刚的判断,很明确就是要把列表扩展到参数指定的最小容量。

private void ensureExplicitCapacity(int minCapacity) 
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    

在这里做判断目的是显而易见的,最小容量至少不能少于当前列表的实际的大小。如果最小容量比列表实际的长度大,则进行扩容操作,调用grow(int minCapacity)函数。

2.4 grow(int minCapacity)函数

该函数用于增加列表的容量以使得容器至少能够包含指定参数的容量大小。

private void grow(int minCapacity) 
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        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);
    

在这里,首先获取数组的实际的长度,然后将长度扩展未原来的1.5倍,如果扩展后的大小小于指定的大小,则新数据的长度为指定的长度;否则为扩展后的长度。同样,程序会判断指定的数组容量是否溢出,如果溢出,再使用hugeCapacity()函数指定数组大小,最后通过copyOf()函数创建新扩展容量的新数组,并把原数组中的数据转到新的数组中。

2.5 hugeCapacity(minCapacity)函数

该函数相当于临界值判断的函数。

private static int hugeCapacity(int minCapacity) 
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    

很明显,数组的长度有两个临界值,一个是0,一个是数组的最大长度。0很好理解,但是对于数组的最大长度,在java的ArrayList的实现中,定义了一个数组的最大长度的静态值,我们来看一下。

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

该值为int的最大值减去8。我们看一下官方解释,所谓的减去8的原因是,有一些虚拟机会在数组中保留一些头部信息,所以保留8作为虚拟机保留的头部信息。

2.6 size()函数

返回该列表真实的保存的数据的长度。

public int size() 
        return size;
    

该函数体现了java的封装性,在ArrayList中,有一个保存列表长度属性的值size,但是该值并不暴露出去,而是封装一个代表相关意义的函数,来获取该值,这样对该size值也是一种保护,用户不能随便修改该值。

2.7 isEmpty()函数

该函数用于判断列表是否为空,其实现方式很简单,直接判断列表长度是否为0即可。

public boolean isEmpty() 
        return size == 0;
    

2.8 contains(Object o)函数

该函数用于判断该列表中是否包含指定的元素。其实现如下:

public boolean contains(Object o) 
        return indexOf(o) >= 0;
    

该函数调用indexOf()函数直接判断其返回值是否大于等于0。下面,我们看一下indexOf函数的实现。

2.9 indexOf(Object o)函数

该函数返回列表中包含元素o的第一个位置,如果元素不存在,返回-1。

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;
    

我们很明显的看到,在这里程序将对象o进行了判断,是否为null。因为其判等的方式是不同的,如果为null,直接可以使用等号进行判断;如果不为null,则需要使用equals进行判断。

这里就引出了另外一个问题,equals为什么可以进行是否相等的判断,而不是使用等号。

这个问题的解答可以看我的另一篇文章《java中equals和等号进行对象相等判断的不同》。

2.10 lastIndexOf(Object o)函数

该函数与上述函数正好相反,返回列表中包含对象o的最后一个位置。其实现逻辑与indexOf()函数也类似,不过就是倒序遍历既可以。

 public int lastIndexOf(Object o) 
        if (o == null) 
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
         else 
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        
        return -1;
    

2.11 clone()函数

该函数实现列表的浅拷贝。在这里,我们学习一下java中的浅拷贝和深拷贝的区别。对于java中对象的浅拷贝和深拷贝可以通过这一篇博客进行学习,该博客将的很透彻Java 浅拷贝和深拷贝的理解和实现方式

我们看一下ArrayList的浅拷贝实现方式:

public Object clone() 
        try 
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
         catch (CloneNotSupportedException e) 
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        
    

同样,其调用父类的clone()函数,然后拷贝数组列表,并将modCount改为0,生成的列表便是一个新的列表。

2.12 toArray()函数

在这里,着重说一下ArrayList列表的toArray()函数,其意思就是将列表转换为对象数组。而在ArrayList中,实现了两个toArray()函数,我们看一下他们两个的返回值。

public Object[] toArray() 
        return Arrays.copyOf(elementData, size);
    
    
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;
    

很明显,我们可以看到,无参数的toArray()函数返回的是对象列表,而有参数的toArray()函数返回的是指定类型的对象列表。所以,如果我们想要通过toArray()函数获取指定类型的数组,则需要使用带有参数的函数。下面是该函数使用的方式的例子:

    ArrayList<String> list = new ArrayList<>();
    list.add("ssss");
    list.add("ddddd");
    
    // 使用方式一
    String strs[] = new String[list.size()];
    System.out.println("第一种方式-----------------");
    list.toArray(strs);
    
    for(int i = 0;i < strs.length;i++)
        System.out.print(strs[i]);
    
    
    System.out.println();
    
    // 使用方式二
    String strs2[] = list.toArray(new String[0]);
    for(int i = 0;i < strs2.length;i++)
        System.out.print(strs2[i]);
    
    
    System.out.println();

2.13 elementData(int index)函数

该函数返回指定位置的元素。

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

在该函数中,我们看到有一个unchecked属性,表明该函数并未对index的范围做相关的验证,其是一个内部函数,为其他函数提供基本支持。

2.14 get(int index)函数

该函数是用户可调用的,用于返回指定的位置的元素。

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

        return elementData(index);
    

函数实现中,首先对传入的index参数做一个范围判断,如果校验成功,则返回指定位置的元素信息。

2.15 rangeCheck(int index)函数

该函数用于对用户传入的指定位置信息进行范围判定,如果超出列表的实际长度或小于0,则抛出IndexOutOfBoundsException。

private void rangeCheckForAdd(int index) 
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    

java基础-arraydeque学习

系列文章目录1、ArrayList-ArrayList函数及相关解释(一)2、ArrayList-ArrayList函数及相关解释(二)文章目录系列文章目录ArrayDeque介绍1、calculateSize2、allocateElements3、doubleCapacity4、copyElements5、构造方法6、add方法7、addLast方法8、offerFirst方法9... 查看详情

类的相关内置函数及反射(代码片段)

类变量的内存位置相关练习1.1classStarkConfig(object):list_display=[]defget_list_display(self):self.list_display.insert(0,33)returnself.list_displayclassRoleConfig(StarkConfig):list_display=[11,22]s1=StarkConfig()r 查看详情

string类及相关函数的实现(代码片段)

#define_CRT_SECURE_NO_WARNINGS#include<iostream>#include<assert.h>#include<string>usingnamespacestd;namespacezzd classstring public: //默认构造一、string(); string() :_str(newchar[1 查看详情

java线程调度及相关函数(代码片段)

文章目录Java线程调度线程调度相关方法代码案例设置线程优先级调用yield暂停当前线程Java线程调度线程调度抢占式调度模型:那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。java采用的就是抢占式调... 查看详情

java线程调度及相关函数(代码片段)

文章目录Java线程调度线程调度相关方法代码案例设置线程优先级调用yield暂停当前线程Java线程调度线程调度抢占式调度模型:那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。java采用的就是抢占式调... 查看详情

fortran调用带有参数且返回类型为数组的函数及相关歧义分析(代码片段)

fortran调用带有参数且返回类型为数组的函数及相关歧义分析1参考书籍2fortran环境3主要思路4具体实现代码4.1项目结构4.2主程序文件(入口文件)4.3外部定义的函数文件4.4测试结果5相关歧义分析5.1分析5.2错误演示代码5.3错... 查看详情

c语言☀️动态内存管理及相关函数(代码片段)

目录一、为什么存在动态内存分配二、动态内存函数 malloc和freecallocrealloc一、为什么存在动态内存分配 之前我们学过的开辟空间的方式就是如下两种:intmain() inta=5; charch='b'; intarr[20]=0;以上的开辟内存的方式... 查看详情

js的prototype属性解释及常用方法(代码片段)

函数:原型每一个构造函数都有一个属性叫做原型(prototype,下面都不再翻译,使用其原文)。这个属性非常有用:为一个特定类声明通用的变量或者函数。prototype的定义你不需要显式地声明一个prototype属性,因为在... 查看详情

arraylist相关面试题(代码片段)

ArrayList线程不安全,线程安全的列表有Vector,Collections.synchronizedList(),CopyOnWriteArrayList,线程不安全的list有:ArrayList、LinkedList。ArrayList扩容倍数是1.5倍。intnewCapacity=oldCapacity+(oldCapaci 查看详情

java——arraylist基本使用(代码片段)

1.简介ArrayList是实现List接口的,底层采用数组实现。ArrayList实现了Cloneable接口,即覆盖了函数clone(),能被克隆。ArrayList实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。方法:... 查看详情

arraylist详细介绍(代码片段)

原文:http://www.cnblogs.com/skywang12345/p/3308556.html第1部分ArrayList介绍ArrayList是一个数组队列,相当于 动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List,RandomAccess,Cloneable,java.io.Serializable这些接... 查看详情

opencv_mat类对象的基本操作常用操作及相关成员函数介绍(代码片段)

OpenCV的Mat类有许多成员函数,我们可以利用这些成员函数实现Mat类对象的基本操作、常用操作。本文列举出Mat类常用的成员函数及其功能。说明:本文提供的示例代码请到页面https://www.hhai.cc/thread-75-1-1.html查看。目录01-成... 查看详情

arraylist不安全的实例及解决方案(代码片段)

ArrayList线程不安全例子:publicclassListTestpublicstaticvoidmain(String[]args)List<String>list=Collections.synchronizedList(newArrayList<>());//List<String>list=newVector<>();//List& 查看详情

malloc和free函数详细解释(代码片段)

一、malloc()和free()的基本概念以及基本用法:1、函数原型及说明:void*malloc(longNumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。(关于分配失败的原因,应该有多... 查看详情

elasticsearch系列---相关性评分算法及正排索引(代码片段)

...,先用booleanmodel将匹配的文档挑选出来,然后再运用评分函数计算相关度,参与的函数如我们提到的TF/IDF、LengthNorm等,再加上一些控制权重的参数设置,得到最后的评分。BooleanModel 查看详情

关于解释matlab代码及相关问题?

(a)如何修改预训练模型的最后三层,解释为什么预训练的预训练模型的最后三层被修改的原因?(b)解释运行结果,并确定可能需要什么来改进结果?(c)对模型的行为做出进一步的假设,并解释这些假设?代码为识别花的代码:clc... 查看详情

基于c++11模板元编程实现scheme中的list及相关函数式编程接口(代码片段)

前言本文将介绍如何使用C++11模板元编程实现Scheme中的list及相关函数式编程接口,如list,cons,car,cdr,length,is_empty,reverse,append,map,transform,enumerate&#x 查看详情

基于c++11模板元编程实现scheme中的list及相关函数式编程接口(代码片段)

前言本文将介绍如何使用C++11模板元编程实现Scheme中的list及相关函数式编程接口,如list,cons,car,cdr,length,is_empty,reverse,append,map,transform,enumerate&#x 查看详情