首页 > 技术文章 > Java8的集合1:ArrayList的实现原理

lgh544 2020-05-14 16:07 原文

1、ArrayList概述

  ArrayList是一个动态数组,实现了List接口以及list相关的所有方法,它允许所有元素的插入,包括null。另外,ArrayListVector了线程不同步之外,大致相等,此实现不同步。

2、属性

//默认容量的大小
private static final int DEFAULT_CAPACITY = 10;

//空数组常量
private static final Object[] EMPTY_ELEMENTDATA = {};

//默认的空数组常量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存放元素的数组,从这可以发现ArrayList的底层实现就是一个Object数组
transientObject[] elementData;

//数组中包含的元素个数
private int size;

//数组的最大上限
private static final intMAX_ARRAY_SIZE = Integer.MAX_VALUE -8;

 

3、构造方法:

  ArrayList()构造一个初始容量为0空列表

  ArrayList(Collection<? extends E> c)构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。

  ArrayList(int initialCapacity)构造具有指定初始容量的空列表

4、方法

 add(E e):

  ArrayList的扩容机制就在这里体现。在插入元素之前,它会先检查是否需要扩容,然后再把元素添加到数组中最后一个元素的后面。在ensureCapacityInternal方法中,我们可以看见,如果当elementData为空数组时,它会使用默认的大小去扩容。所以说,通过无参构造方法来创建ArrayList时,它的大小其实是为0的,只有在使用到的时候,才会通过grow方法去创建一个大小为10的数组。时间复杂度度为O(1)

 

 

 

 

 

 

 set(int index,E element)

  set方法的作用是把下标为index的元素替换成element,跟get非常类似,时间复杂度度为O(1)

remove(int index)

    remove方法与add带指定下标的方法非常类似,也是调用系统的arraycopy方法来移动元素,时间复杂度为O(n)

grow(int minCapacity)

  grow方法是在数组进行扩容的时候用到的,ArrayList每次扩容都是扩1.5倍,然后调用Arrays类的copyOf方法,把元素重新拷贝到一个新的数组中去。

size()

  size方法非常简单,它是直接返回size的值,也就是返回数组中元素的个数,时间复杂度为O(1)。这里要注意一下,返回的并不是数组的实际大小。

indexOf(Object o)lastIndexOf(Object o)

  indexOf方法的作用是返回第一个等于给定元素的值的下标。它是通过遍历比较数组中每个元素的值来查找的,所以它的时间复杂度是O(n)lastIndexOf的原理跟indexOf一样,而它仅仅是从后往前找起罢了

Vector

  Vector很多方法都跟ArrayList一样,只是多加了个synchronized来保证线程安全。VectorArrayList的不同点:

  VectorArrayList多了一个属性:

 

protected int capacityIncrement;

 

  这个属性是在扩容的时候用到的,它表示每次扩容只扩capacityIncrement个空间就足够了。该属性可以通过构造方法给它赋值。先来看一下构造方法:

  从构造方法中,我们可以看出Vector的默认大小也是10,而且它在初始化的时候就已经创建了数组了,这点跟ArrayList不一样。

 

 

 

 再来看一下grow方法:

 

   grow方法中我们可以发现,newCapacity默认情况下是两倍的oldCapacity,而当指定了capacityIncrement的值之后,newCapacity变成了oldCapacity+capacityIncrement

还有些方法放在最后的代码里面来体现。

5、总结

  1ArrayList创建时的大小为0;当加入第一个元素时,进行第一次扩容时,默认容量大小为10

  2ArrayList每次扩容都以当前数组大小的1.5倍去扩容。

  3Vector创建时的默认大小为10

  4Vector每次扩容都以当前数组大小的2倍去扩容。当指定了capacityIncrement,每次扩容仅在原先基础上增加capacityIncrement个单位空间。5ArrayListVectoraddgetsize方法的复杂度都为O(1)remove方法的复杂度为O(n)6ArrayList是非线程安全的,Vector是线程安全的。

package util;

import java.util.ArrayList;
import java.util.Arrays;

public class ArrayListMethod {

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        
        //add(E e) add(int index, E e)
        list.add(1);
        list.add(2);
        list.add(0,0);//下标为0的数值设置为0;
        list.add(0);
        System.out.println("添加数据是否成功\t"+list.add(3));//添加数据成功返回true
        
        //set(int index,E element)
        int oldValue = list.set(1,8);//将下标为1的数据改为8,并返回修改前的数据的值
        System.out.println("修改下标为1的值为8之后原来的数据:\t"+oldValue);
        
        System.out.println("打印:\t"+list);
        
        //remove(int index)
        //删除下标为2的数据,并返回删除的数据的值
        int removeData = list.remove(2);
        System.out.println("删除的数据是:\t"+removeData);
        
        System.out.println("打印:\t"+list);
        
        //size(),isEmpty(),contains()
        System.out.println("大小:\t"+list.size());
        System.out.println("是否为空:\t"+list.isEmpty());
        System.out.println("包含数据2吗:\t"+list.contains(2));//包含数据2吗
        
        //indexOf(Object o),lastIndex(Object o)
        System.out.println("数据8的下标:\t"+list.indexOf(8));//寻找8的下标
        System.out.println("数据0的最后一位的下标:\t"+list.lastIndexOf(0));
        System.out.println("数据0的第一位下标:\t"+list.indexOf(0));
        System.out.println("下标为1的数据是:\t"+list.get(1));//寻找下标为1的数
    
        //clone()
        Object listClone = list.clone();//克隆
        System.out.println("克隆的数据:\t"+listClone);
        
        //toArray()
        //转换为Object类型数组
       Object listArray[] = list.toArray();
       System.out.print("打印转化为Object类型的数组:\t");
        for(Object l:listArray) {
            System.out.print(l+" ");
            
        }
        System.out.println();
        
        //转换为Integer类型的数组
        //Integer[] a = new Integer[list.size()];
           Integer[] list1 = list.toArray(new Integer[list.size()]);//转换为数组
           System.out.print("打印转化为Integer类型的数组:\t");
           for(int l:list1) {
                System.out.print(l+" ");
            }
           System.out.println();
           
           //addAll()
           //添加一个集合
//        boolean flag = list.addAll(list);
//        System.out.println(flag);
//        System.out.println(list);
//        
//        //指定位置加入一个集合
//        boolean flag1 = list.addAll(1, list);
//        System.out.println(flag1);
//        System.out.println(list);
        
           //clear(),removeAll()
            list.clear();
            list.removeAll(list);
          System.out.println("清空\t"+list);
           
           
    }
    

}

运行结果

 

 

 

推荐阅读