首页 > 其他分享 >ArrayList

ArrayList

时间:2024-07-13 23:21:09浏览次数:5  
标签:index return int ArrayList elementData minCapacity size

创建ArrayList

  • 不指定初始大小
List<String> list = new ArrayList<>();

调用无参构造方法,创建一个初始容量为10的空列表

private static final int DEFAULT_CAPACITY = 10;

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
  • 指定初始大小,避免在添加新的元素时进行不必要的扩容
List<String> list = new ArrayList<>(20);

添加元素

list.add("hh");
  • 源码
public boolean add(E e) {
    // 确保容量够容纳新的元素
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 在ArrayList末尾添加新的元素
    elementData[size++] = e;
    return true;
}
// 列表中已有的元素个数,此时为0
private int size;
// 此时minCapacity为1
private void ensureCapacityInternal(int minCapacity) {
    // elementData 为存放 ArrayList 元素的底层数组,此时为空
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 默认初始容量是10
private static final int DEFAULT_CAPACITY = 10;

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 从10和1中选最大的,返回10
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
// 此时minCapacity为10
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    // 需要的容量为10,此时用于存储列表数据的实际大小为0,需要扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
private void grow(int minCapacity) {
    // overflow-conscious code
    // 当前实际大小
    int oldCapacity = elementData.length;
    // 扩容到原来的1.5倍
    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:
    // 将数组复制到一个长度为newCapacity的新数组中
    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;
}
  • ArrayList在第一次执行add后会扩容为10,在添加第11个元素的时候会第二次扩容

向指定位置添加元素

public void add(int index, E element) {
    // 越界判断
    rangeCheckForAdd(index);

    // 确保容量够,不够就扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 将index及其后面的元素后移一位,把index下标处留给新元素
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    // 插入到index下标处
    elementData[index] = element;
    // 元素个数加一
    size++;
}

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

更新元素

public E set(int index, E element) {
    // 越界检查
    rangeCheck(index);
    // 替换上新元素,返回旧元素
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

删除元素

  • 删除指定位置的元素
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);
    // 数组末尾元素置为null,让GC回收该元素占用的空间
    elementData[--size] = null; // clear to let GC do its work
    // 返回被删除的元素
    return oldValue;
}
  • 删除指定元素
public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            // null 的时候使用 == 操作符判断
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            // 非 null 的时候使用 equals() 方法
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

private void fastRemove(int index) {
    modCount++;
    // 计算需要移动位置的元素总数
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    // 数组末尾置空,并让GC回收空间
    elementData[--size] = null; // clear to let GC do its work
}

查找元素

list.indexOf("haha");
list.lastIndexOf("haha");
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;
}
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;
}
// contains()内部就是通过indexOf()实现的
public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

二分查找

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("c");
    list.add("e");
    list.add("d");
    // Collections 类的 sort() 方法可以对 ArrayList 进行排序
    // 如果是自定义类型的列表,还可以指定 Comparator 进行排序。
    Collections.sort(list);
    // [c, d, e]
    System.out.println(list);
    // 2
    System.out.println(Collections.binarySearch(list, "e"));
}

时间复杂度

  • ArrayList内部使用数组来存储元素
操作 最好 最坏
查询 O(1) O(1)
插入 O(1) O(n)需要将插入位置之后的元素全部向后移动一位
删除 O(1) O(n)需要将删除位置之后的元素全部向前移动一位
修改 O(1) O(1)

拷贝

// 返回该列表的浅表副本,元素本身不会被复制
// 浅表副本是创建一个新的对象,然后将当前对象的非静态字段复制到该新对象。
// 如果字段是值类型的,则对该字段执行逐位复制。
// 如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其副本引用同一对象。
public Object clone() {
    try {
        // 调用 Object 类的 clone 方法,得到一个浅表副本
        ArrayList<?> v = (ArrayList<?>) super.clone();
        // 复制 elementData 数组,创建一个新数组作为副本
        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);
    }
}

序列化

  • 使用了 ArrayList 的实际大小 size 而不是数组的长度(elementData.length)来作为元素的上限进行序列化
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    // 写出对象的默认字段
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    // 写出 size
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        // 依次写出 elementData 数组中的元素
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    // 读取默认字段
    s.defaultReadObject();

    // Read in capacity
    // 读取容量,这个值被忽略,因为在 ArrayList 中,容量和长度是两个不同的概念
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        // 分配一个新的 elementData 数组,大小为 size
        int capacity = calculateCapacity(elementData, size);
        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        // 依次从输入流中读取元素,并将其存储在数组中
        for (int i=0; i<size; i++) {
            // 读取对象并存储在 elementData 数组中
            a[i] = s.readObject();
        }
    }
}

标签:index,return,int,ArrayList,elementData,minCapacity,size
From: https://www.cnblogs.com/sprinining/p/18300956

相关文章

  • 2-ArrayList底层结构和源码分析
    2-ArrayList底层结构和源码分析介绍汇总:ArrayList的注意事项ArrayList的运行重要步骤补充1-ArrayList的注意事项ArrayList允许添加所有的元素,包括null,而且还可以多个null。ArrayList是由数组来实现数据存储的。ArrayList基本等同于Vector,除了ArrayList是线......
  • HashTable,ArrayList,queue等常用方法
    HashTable,ArrayList,queue等常用方法HashMap是Java中非常常用的集合类,它存储键值对,并提供了一系列方便的方法来操作这些数据。以下是一些HashMap的常用方法:1.添加和获取元素:put(key,value):将指定的键值对添加到HashMap中。如果键已存在,则更新对应的值。get(ke......
  • ArrayList底层结构和源码分析
    //无参构造器创建ArrayList对象//ArrayListlist=newArrayList();//断点1ArrayListlist=newArrayList(8);//断点2//添加1-10数据for(inti=0;i<=10;i++){list.add(i);}//添......
  • ArrayList和LinkedList的比较
    基本比较 底层结构增删效率改查效率ArrayList可变数组较低;数组扩容较高LinkedList双向链表较高,通过链表追加较低选择使用若改查操作多选择ArrayList增删操作多选择LinkedList通常程序中大部分操作为查询,因此通常使用ArrayList根据需求,效率优先的原则......
  • 一分钟轻松掌握Java的Vector&ArrayList
    Vector方法是同步的,线程安全ArrayList方法是非同步的,效率较高向量变量的声明格式Vector<向量元素的数据类型>变量名;示例Vectorvs;创建向量实例对象Vectorvs=newVector();在Java中,Vector<Object>是一个泛型Vector,它专门用于存储Object类型或其子类型的对象......
  • ArrayList并发修改异常
    遇到这么个问题:迭代器并发修改会抛出异常,这个问题之前没有遇到过,特意记录一下: publicstaticvoidmain(String[]args){//创建集合对象List<String>list=newArrayList<String>();//添加元素list.add("hello");list.add("Java......
  • ArrayList顺序表简单实现
    一、创建MyArrayList框架 1.1MyArrayList类中实现 arr数组importjava.util.Arrays;publicclassMyArrayList{privateint[]arr;privateintusesize;privatestaticfinalintP=10;publicMyArrayList(){arr=newint[P];......
  • C#——动态数组ArrayList
    动态数组动态数组:ArrayList,代表了可被单独索引的对象的有序集合,可以代替一个数组Array,动态数组可以使用索引在指定的位置添加或者删除元素,动态数组自动重新调整数组的大小声明声明方式1:不带参数初始数组 ArrayLista1=newArrayList();声明方式2:初始化的带上数......
  • ArrayList、LinkedList
    区别ArrayList是实现了基于数组的数据结构,内存连续;LinkedList是基于链表结构。对于随机访问的get和set方法查询元素,ArrayList要优于LinkedList,因为LinkedList循环链表寻找元素。对于新增和删除操作add和remove,LinkedList比较高效,因为ArrayList要移动数据。优缺点ArrayLis......
  • java:数组和集合(例如ArrayList)的对比
    问题:为什么java里有了array还要有arrayList?(相类比的:python里只有list没有array)答案:因为arrayList是对array的补充,更灵活实用。数组和arrayList都是一维的,但数组可以通过下标直接访问,arrayList只能通过遍历访问;数组能存储基本类型和对象,arrayList只能存对象;数组长度不可变,array......