Java学习笔记十八-ArrayList源码分析

最近学习了ArrayList类,自己试着编写了其中常用的函数,发现和源码差的不是一点点...

在类的属性中,定义了size属性,记录数组的长度。以及elementData空数组

 

/*    获得数组的长度  */

public int size() {
        return size;
    }

/*   判断数组是否为空  */

public boolean isEmpty() {
        return size == 0;
    }

/*   获得元素的索引位  */

public int indexOf(Object o) {                      //返回传入参数的索引位
        if (o == null) {                            //当传入参数为空时,遍历数组是否有元素为空
            for (int i = 0; i < size; i++)          //防止equals函数报错
                if (elementData[i]==null)
                    return i;                       //返回位置
        } else {                                    //传入参数不为空时遍历数组,找到匹配元素
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;                       //相等则返回位置
        }
        return -1;                                  //找不到则返回-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;
    }

/*  返回指定索引位的元素   */

 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];
    }

/*   更改指定索引位的数据,并返回原值  */

public E set(int index, E element) {               //设置索引位index的数据值为element
        rangeCheck(index);                               //检查索引是否越界

        E oldValue = elementData(index);            //将原值强制转换为泛型
        elementData[index] = element;               //赋值
        return oldValue;                                     //返回原值
    }

/*   向数组尾添加元素  */

public boolean add(E e) {
        ensureCapacityInternal(size + 1);   // Increments modCount!!  将最小容量设置为当前长度加1,便于之后扩容
        elementData[size++] = e;            //扩容之后将元素添加到末尾即可
        return true;                        //返回添加成功
    }
private void ensureCapacityInternal(int minCapacity) {                     //确保内部容量
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {            //如果数组现在是空的话
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);         //最小值为默认容量和最小容量的最大值(保证数组至少是默认容量大小)
        }                                                                  //minCapacity表示所需的最小容量

        ensureExplicitCapacity(minCapacity);
    }
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;                                               //修改次数加一

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)                 //如果最小容量大于数组目前长度,则需要扩容
            grow(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);     //将旧数组改变为新容量大小的数组,即实现扩容
    }
private static int hugeCapacity(int minCapacity) {                //设置最大容量函数
        if (minCapacity < 0) // overflow                          //如果最小容量小于0,抛出溢出错误
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?                   //如果最小容量大于最大数组容量,则返回整数的最大值
            Integer.MAX_VALUE :                                   //否则返回设定的最大数组容量
            MAX_ARRAY_SIZE;
    }

/*  向指定索引位添加元素  */

public void add(int index, E element) {
        rangeCheckForAdd(index);                                        //为了数组添加的越界检查
 
        ensureCapacityInternal(size + 1);  // Increments modCount!!  与上一个add相同,数组扩容
        System.arraycopy(elementData, index, elementData, index + 1,size - index);   //将索引位前后分别复制到一个新数组里,留下索引位空位
        elementData[index] = element;                                                //向索引位加入元素
        size++;
    }
 private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)                                  //如果索引位大于数组长度或者小于0,抛出索引越界异常
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

/*   移除索引位的元素并返回   */

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 清空最后一个空位,让GC回收

        return oldValue;
    }

/*  移除数组里的某个元素     removeAll方法需要传入一个集合对象  */

public boolean remove(Object o) {
        if (o == null) {                                       //如果要移除的数据为空的话
            for (int index = 0; index < size; index++)         //遍历数组,如果有为空的元素,则移除该元素
                if (elementData[index] == null) {             
                    fastRemove(index);                         //与上remove操作类似
                    return true;
                }
        } else {                                              //如果要移除的数据不是空
            for (int index = 0; index < size; index++)        //遍历数组,获得相等的第一个元素则移除,与上操作相同
                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);
        elementData[--size] = null;                      // clear to let GC do its work
    }

/*   清空数组   */

 public void clear() {
        modCount++;                              //修改次数增加

        // clear to let GC do its work
        for (int i = 0; i < size; i++)           //遍历数组均置为空,让GC回收
            elementData[i] = null;

        size = 0;                                //数组尺寸设为0
    }

/*   添加集合中的全部元素到数组尾   */

  public boolean addAll(Collection<? extends E> c) {               //泛型
        Object[] a = c.toArray();                                  //将集合c转换为数组a
        int numNew = a.length;                                     //获得a的长度
        ensureCapacityInternal(size + numNew);                     // Increments modCount 将数组长度至少设置为size+numNew便于扩容 
        System.arraycopy(a, 0, elementData, size, numNew);         //将一个新长度的数组赋给原数组
        size += numNew;                                            //数组有效尺寸增加
        return numNew != 0;
    }

/*  在指定索引位增加集合 */

public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);                                        //为添加检查索引位是否越界

        Object[] a = c.toArray();
        int numNew = a.length;                                          //与上操作相同
        ensureCapacityInternal(size + numNew);                          // Increments modCount

        int numMoved = size - index;                                   //获得需要移动的数据个数
        if (numMoved > 0) 
            System.arraycopy(elementData, index, elementData, index + numNew,      //重新获得数组,留出中间的空位
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);             //将a数组赋值到空位中
        size += numNew;                                                 //数组有效位数增加
        return numNew != 0;
    }

/*  将数组两个索引位之间的数据取出为List形式    

     注意:subList包前不包后,截取的是fromIndex到toIndex之前的部分

  */

public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);                     //检查子链的两个索引位是否符合要求
        return new SubList(this, 0, fromIndex, toIndex);                 //返回从fromIndex到toIndex的子链  SubList是一个内部类
    }
static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0)                                                       //第一个索引位小于0则抛出异常
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > size)                                                      //第二个索引位大于数组尺寸则抛出异常
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)                                                 //第一个索引位大于第二个索引位抛出异常
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
    }

/*

  *在这个函数中,操作的还是原来的集合,即对截取出来的子集进行处理时,原集合也会改变。虽然子集

  *与子集的地址不是同一个,但是指向的是同一个对象

  */

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ArrayListJava中的一个类,它可以动态地存储一组元素,可以根据需要动态增加或减少元素的数量,是一种非常方便的数据结构。 ArrayList的基本用法包括创建ArrayList对象、添加元素、获取元素、修改元素、删除元素等。首先,可以使用如下代码创建一个空的ArrayList对象: ```java ArrayList<String> list = new ArrayList<String>(); ``` 上面代码创建了一个类型为String的ArrayList对象,该对象初始为空。然后,可以使用add()方法向ArrayList中添加元素,例如: ```java list.add("apple"); list.add("banana"); list.add("orange"); ``` 上述代码向list中添加了三个字符串元素。可以使用get()方法获取ArrayList中的元素,例如: ```java String first = list.get(0); // 获取第一个元素 ``` 可以使用set()方法修改ArrayList中的元素,例如: ```java list.set(1, "pear"); // 将第2个元素改为"pear" ``` 可以使用remove()方法删除ArrayList中的元素,例如: ```java list.remove(2); // 删除第3个元素 ``` 以上就是ArrayList的基本用法。需要注意的是,ArrayList中的索引从0开始。 ### 回答2: ArrayListJava中非常常用的数据结构。它提供了一个可以动态添加、删除、修改的可变长度的序列。 使用ArrayList时,首先需要引入它的包:java.util。然后可以使用如下语法创建一个ArrayList对象: ```java ArrayList<String> list = new ArrayList<String>(); ``` 这里的`<String>`说明了这个ArrayList中的元素类型是String。当然,也可以使用其他类型作为元素类型。例如: ```java ArrayList<Integer> numbers = new ArrayList<Integer>(); ArrayList<Double> prices = new ArrayList<Double>(); ``` 可以使用`add()`方法来向ArrayList中添加元素: ```java list.add("apple"); list.add("orange"); list.add("banana"); ``` 可以使用`get()`方法来获取指定位置的元素: ```java String fruit = list.get(1); //获取第二个元素,即"orange" ``` 可以使用`size()`方法来获取ArrayList中元素的个数: ```java int size = list.size(); //获取ArrayList中元素的个数 ``` 可以使用`set()`方法来修改指定位置的元素: ```java list.set(1, "pear"); //将第二个元素修改为"pear" ``` 可以使用`remove()`方法来删除指定位置的元素: ```java list.remove(2); //删除第三个元素,即"banana" ``` 需要注意的是,ArrayList中的元素是有序的,且可以重复。因此,可以使用循环来遍历ArrayList中的元素: ```java for(int i=0; i<list.size(); i++){ String fruit = list.get(i); System.out.println(fruit); } ``` 或者使用增强型循环(foreach): ```java for(String fruit : list){ System.out.println(fruit); } ``` 总之,使用ArrayList可以方便地处理可变长度的序列。在实际开发中,它有着广泛的应用场景,例如处理文件或数据库中的数据,实现算法或数据结构等。 ### 回答3: ArrayListJava中一个非常常用的容器类。他的优点是可以存储任意类型的对象,可以动态扩容,因此在使用上非常的方便。 使用ArrayList需要在代码中首先调用import java.util.ArrayList进行导入,随后可以通过ArrayList<类型> name = new ArrayList<类型>()这个语句声明一个ArrayList,并将其命名为name,同时指定ArrayList中存储的对象类型为类型。当我们需要添加元素时,可以通过name.add(element)将元素添加到ArrayList中。我们也可以通过name.get(index)方法获取ArrayList中指定位置的元素,通过name.set(index,value)方法将ArrayList中某个位置的元素替换为新的元素。同时,我们也可以调用name.size()方法获取ArrayList中元素的数量。 值得注意的是,ArrayList中的元素是以索引的方式存储的,这意味着我们可以根据元素的位置进行添加、修改、删除等操作。而且,由于ArrayList的容量是可变的,因此其内部必须动态地管理数据的内存,这会影响到ArrayList的性能。当然,这个影响是很小的,不会对代码的运行产生显著的影响。 总之,ArrayListJava中非常常用的容器类,其可以存储任意类型的对象,同时调用也非常方便。但在使用时需要注意其操作的复杂度,以及不能存储基本数据类型。如果需要在ArrayList中存储基本数据类型,需要借助Boxing和Unboxing机制将其转换为对应的包装类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值