arrayList这一篇就够了,详解arrayList基础原理

在arraylist当中实现了三个标记接口和继承了一个父类

标记接口

空接口,以Serializable接口为例,如果一个类实现了这个接口,则表示这个类可以被序列化。因此,我们实际上是通过了Serializable这个接口给该类标记了【可被序列化】的元数据,打上了【可被序列化】的标签。这也是标记/标签接口名字的由来。

1.RandomAccess

表示该实现类支持随机访问(下标访问)

2.Cloneable

表示支持拷贝

再ArrayList中需要扩容时,创建一个原数组的大小1.5倍的数组,然后将原数组拷贝到新数组

实现Cloneable 并 重写Object中的clone()方法     调用这个方法可以返回一个当前对象的拷贝

如果没有做一些特殊操作就是普通的浅拷贝 只拷贝基本属性的值  引用属性就通用一个引用

3.Serializable

表示支持序列化    将当前对象进行写入本地或者网络传输

解析ArrayList源码

arrayList中基于elementData来进行一系列的操作,transient表示不参与序列化或者说是不被序列化

在新建ArrayList的时候会根据构造器的不同初始不同的常量空数组

不带参:赋值DEFAULTCAPACITY_EMPTY_ELEMENTDATA常量给elementData

将会再第一次做添加时进行创建一个10个大小的数组,当满了时就会以1.5倍进行增长

(每次增长都是一次数组的复制(消耗内存),如果大概知道当前集合存储的数据大小大概可以初始化时带大小参数)

带参:如果是int并且不等0,或者是传入集合不等空 就赋值EMPTY_ELEMENTDATA常量 给elementData 作为一个标识 也可以合理的节省内存

int不为0  就创建一个int大小的数组赋值给elementData

集合不为空 就将集合赋值给elementData

 

arrayList中sizi表示里面的数据条数      elementData.length表示数组的大小

 

get(int index)

获取数据

基于数组的操作来实现        查询快

因为数组支持随机查询,当创建一个数组时,堆中创建一个长度固定 持续内存 每个元素占用的内存空间是相同

get(i)      数组的起始位置+i*元素占用的内存       就可以算出get(i)的位置

add(E e)

添加数据

public boolean add(E e) {
    	//执行记录modCount计入数  和判断是否扩展数组
        ensureCapacityInternal(size + 1);  
    	//做完上述操作就可以直接赋值
        elementData[size++] = e;
        return true;
}
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //判断是否为不带参创建的集合
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
    	//false为还有空间  不用扩容
        if (minCapacity - elementData.length > 0)
            //扩容   (数组拷贝)
            grow(minCapacity);
}
private void grow(int minCapacity) {
        //拷贝操作
        int oldCapacity = elementData.length;
        //(oldCapacity >> 1)位运算   更快的将一个数除以2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
    	//集合最大值newCapacity     Integer类型最大值-8(MAX_ARRAY_SIZE)                                 
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
}

add(int index, E element)

根据下标添加数据

先理解System.arraycopy的用法详解

public static void main(String[] args) {
        int[] src = {1, 2, 3, 4};
        int[] dest = new int[5];
        System.arraycopy(src, 0, dest, 1, 4);

        for (Object o : dest) {
            System.out.println(o);
        }
}
/*
        打印结果   0 1 2 3 4
*/

src = |1|2|3|4|
dest = |0|0|0|0|0|
执行System.arraycopy(src, 0, dest, 1, 4);时
第一步:从源数组(src)中,从下标0开始取,取4个,也就是src[0]-src[3],即1 2 3 4四个数
第二步:把取出的数,按顺序,存放到目标数组(dest)中,从下标1开始存,存4个,也就是dest[1]-dest[4]。
所以数组dest为:|0|1|2|3|4|

public void add(int index, E element) {
    //先判断下标是否正常,大于等于0,不超过size
    rangeCheckForAdd(index);
	//执行是否扩容
    ensureCapacityInternal(size + 1);  
    //将index到elementData.length的数据移到index+1到elementDate.length
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

并不是一个一个去向后移,而是一次复制到后一位

remove(int index)

按照下标移除数据

public E remove(int index) {
    //检查下标是否越界
    rangeCheck(index);
	//计入操作数
    modCount++;
    //计入老值
    E oldValue = elementData(index);
	//判断index是否为最后一个操作数
    int numMoved = size - index - 1;
    if (numMoved > 0)
        //如果不是   就从index到size区间向前一步移
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //移除最后一位 
    elementData[--size] = null; 

    return oldValue;
}

也是使用了复制(arraycopy)将index到size的位置向前移动一步,然后清除最后一位

remove(Object o)

按照数据移除数据

public boolean remove(Object o) {
    //值为空的
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                //类似于按下表删除的操作
                fastRemove(index);
                return true;
            }
    } else {
        //不为空
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                //类似于按下表删除的操作
                fastRemove(index);
                return true;
            }
    }
    return false;
}

简单的进行两个循环操作,先找到元素,然后根据下标移除

Iterator<E> iterator()

迭代器

用arrayList.iterator();的时候会创建一个内部类的对象

subList(int fromIndex, int toIndex)

public List<E> subList(int fromIndex, int toIndex) {
    //先验证fromIndex 和 toIndex   是否越界
    subListRangeCheck(fromIndex, toIndex, size);
    //调用SubList()有参构造    SubList是一个内部类   
    return new SubList(this, 0, fromIndex, toIndex);
}

SubList是一个内部类继承自AbstractList<E>里面有一些常用的arraylist方法(并不是全部),当调用SubList的带参构造就会创建一个arraylist的副本而且也会记录当前的modCount,如果原数组modCount改变,操作SubList的对象就会报错

Arrays.asList()

将一个数组转换为list      这个数组如果是基本类型就会转换为一个二维数组,因为泛型不支持基本类型,它会吧基本类型的数组当作一个对象存入进去

不可变集合(只读)

Collections.unmodifiableList(arraylist)   会返回一个list    这个list只能被读取     

当原集合被改变时,只读集合也会跟着改变

拓展

解析Vector

当被创建的时候直接创建一个10个大小的数组  

每次扩容为一倍

所有方法都加了Synchronized锁   效率极差  只能保证单个操作安全,复合操作同样不安全

并没有锁全局  只是每个方法都加了锁  单一原子性         

两个线程一个进行循环删除    一个进行循环读取     或导致下标越界因为这是两个方法两把锁并没有互相干涉

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值