基础数据结构——ArrayList(下)

目录

1.前言

2.模拟实现ArrayList

3.ArrayList的使用及部分注意事项


1.前言

在上一篇文章中我们已经大致了解了ArrayList的一些基本用法和ArrayList的部分模拟实现,而在本文我们将继续完成上文未完成的模拟ArrayList的代码,并和大家讲解一些ArrayList使用的一些注意事项。上一篇文章链接:基础数据结构——ArrayList(上)_invictusQAQ的博客-CSDN博客

2.模拟实现ArrayList 

上一篇文章未实现的功能已放在下文:

public int get(int pos){//获取pos位置元素
            if(!checkPosInGet(pos)){
                throw new MyArrayListIndexException("获取下标不合法!");
            }
            return this.elem[pos];
        }

        public void remove(int key){
            if(isEmpty()){
                throw new MyArrayListEmptyException("顺序表为空!");
            }
            int index=indexOf(key);
            if(index==-1){
                throw new MyArrayListIndexException("该元素不存在!");
            }
            else{
                for(int i=index;i<this.usedSize-1;i++){
                    this.elem[i]=this.elem[i+1];
                }
            }
            this.usedSize--;
            // this.elem[usedSize] = null; 如果是引用类型 这里需要置为空
        }


        // 获取顺序表长度
        public int size() {
            return this.usedSize;
        }

        public void clear(){
        /*
        如果是引用数据类型 得一个一个置为空
        for (int i = 0; i < this.usedSize; i++) {
            this.elem[i] = null;
        }
        this.usedSize = 0;
        */
            this.usedSize=0;
        }

        public void set(int pos,int value){
            if(!checkPosInGet(pos)){
                throw new MyArrayListIndexException("下标位置不合法!");
            }
            this.elem[pos]=value;
        }

完整代码我将上传至gitee: ArrayList: 有关ArrayList实现的数据结构

3.ArrayList的使用及部分注意事项

1.

首先我们知道ArrayList即使我们不初始化给他一个大小他也是有一个默认的大小的,但是他并非在我们创建对象时就已经分配好了默认大小,事实上在创建完ArrayList时他默认为空只有当我们在使用了add方法后他才会被分配内存空间。

我们先看到下面一段源码代表的意思:

//默认容纳容量
private static final int DEFAULT_CAPACITY = 10;
//默认空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认容纳容量下的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存储数据的数组,定义为Object是为了容纳泛型
transient Object[] elementData; 
//数组存储的实际内容大小
private int size;
//最大数组大小,数组为int类型的最大值-8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

在我们new一个ArrayList对象时,我们转到源码可以看到如下代码

 可以看到如果我们不执行add方法那么他默认就是一个空的数组,而当我们看到add源码时,他首先会执行下面的一个方法

add方法先调用ensureCapacityInternal方法来保证容纳容量可用,随后在指定的数组位置赋值为传入参数,随后size自增1来添加1个元素。

 然后看下ensureCapacityInternal方法。

他的作用是计算容量是否需要扩容

这里可以看到这个方法里面调用了很多方法,由内向外看一下具体方法的作用。
先看calculateCapacity方法。

 

 这个方法的实际作用翻译一下就是

1.判断数组是否为默认容纳容量下的空数组(实际上数组如果为默认空数组结果也是一样的,因为二者实际内容相同)

2.是则返回默认容量和传入的最小容量两者之间的较大值

3.否则返回最小容量

calculateCapacity方法实际上是判断数组是否为空然后计算其对应的容量,若数组不为空则直接返回传入的最小容量。

然后来看ensureExplicitCapacity方法。 

这个方法可以简单理解为判断是否需要扩容

接下来终于进入了真正的扩容方法grow,以下是他的源代码

private void grow(int minCapacity) {
    //将数组容纳容量赋值给oldCapacity
    int oldCapacity = elementData.length;
    //将数组容纳容量扩至原来的1.5倍的值赋给newCapacity
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //判断newCapacity和minCapacity的大小关系
    if (newCapacity - minCapacity < 0)
    	//newCapacity小于minCapacity则将minCapacity的值赋给minCapacity
        newCapacity = minCapacity;
    //判断newCapacity和最大数组大小的大小关系
    if (newCapacity - MAX_ARRAY_SIZE > 0)
    	//调用hugeCapacity方法,传入参数为minCapacity,将得到的值赋给newCapacity
        newCapacity = hugeCapacity(minCapacity);
  	//调用Arrays.copyOf方法扩容数组
    elementData = Arrays.copyOf(elementData, newCapacity);
}

可以看到grow的扩容是1.5倍扩容,相对于我们自己模拟实现的2倍扩容在一定程度上减少了空间的浪费。

2.

import java.util.*;

public class TestDemo {
    public static void main(String[] args) {

        ArrayList<Integer> arrayList=new ArrayList<>();
        arrayList.add(5);
        arrayList.add(4);
        arrayList.add(3);
        arrayList.add(2);
        arrayList.add(1);
        System.out.println(arrayList);
        arrayList.remove(1);
        arrayList.remove(new Integer(1));
        System.out.println(arrayList);
    }
}

现在我们看到这一串代码,众所周知remove方法是删除ArrayList中下标为(x)的元素,但是有的时候我们想删除某个特定的元素又该怎么使用remove方法呢?其实我们只需要把你要删除的那个对象的引用传进去就好,比如

arrayList.remove(new Integer(1)); 这个操作就是删除1这个元素,运行结果如下

 

 3.

我们知道ArrayList中也是有截取方法sublist的,而当我们在上述代码中执行以下代码时

System.out.println(arrayList.subList(1,3));

 可以发现sublist方法的截取区间是一个左闭右开的区间

但是sublist是直接截取一个新的部分还是只是返回了被截取部分的引用呢?看到下面的代码

import java.util.*;

public class TestDemo {
    public static void main(String[] args) {

        ArrayList<Integer> arrayList=new ArrayList<>();
        arrayList.add(5);
        arrayList.add(4);
        arrayList.add(3);
        arrayList.add(2);
        arrayList.add(1);
        System.out.println(arrayList);
        List<Integer> tmp = arrayList.subList(1, 3);
        tmp.add(99);
        System.out.println(arrayList);
    }
}

 他的运行结果如下:

 可以看到我们对sublist截取的部分进行修改原数组也发生了变化,证明sublist是在其本身进行截取的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值