手撕ArrayList源码

手撕ArrayList源码

一、背景(罗素悖论)

一天,村里的理发师挂出了一个牌子
“村里所有不给自己理发的人,都由我来理发,我也只给这些人理发”
有人问理发师:你自己的头发谁来理?

罗素:英国哲学家、数学家,1902年提出,撼动了数学界的基石。
集合论:现代数学的基石,数学家康托尔,德国人,有着犹太的血统。
开始是研究三角函数,逐渐摸索并提出了集合论,引来巨大的争议,
包括他的老师,逐渐抑郁,以致精神失常,落寞地病逝于精神病院。

当前最聪明的三大人种:犹太人、日耳曼人、亚裔人(中国人)
电影《美丽心灵》 博弈论的提出者 纳什 的个人传记,一生都于精神分裂
抗争,最终获得诺贝尔经济学奖。

二、集合这些事儿(三大块)

List 有顺序可重复的 ArrayList
Set 无顺序不可重复的 HashSet
Map 键值对 key-value HashMap

三、ArrayList

如果存储渡一所有的成员,用什么?
100 - 1000 - 10000 (容器:动态的,快速查询)

1)概述 (60分)

array 数组(本质) list列表
数组:int[] arr = new int[3] 初始化需要定义长度,且长度不能更改
动态数组:arraylist解决数组长度固化的问题,同时带来新的问题。
判断容量不足时,进行扩容,扩容算法?

数组的特性:内存空间是连续存储的,随机访问的效率非常之高。
封装为arraylist后,增加和删除的效率就降低了。
增加时,可能需要扩容;删除时,可能需要移动

面试题:

a arraylist是如何扩容的?
b 如果要加入1w条数据,如何提高效率? (80分)
c 它是线程安全的吗?如何做到线程安全?
d fail_fast是什么? (100分

2)源码分析(1.8版本)
a 类声明
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

i 继承了AbstractList并实现List , 需要提供最基本的增删改查功能
ii 实现了RandomAccess,支持随机访问/快速访问,和数组的访问方式一致
iii 实现了Cloneable,能够被克隆
iv 实现了Serializable,可以被序列化,如果需要自定义序列化,需要实现writeObject和readObject方法

b 构造器 (alt+7 查看方法列表)

i 无参
//存储所有数据的对象数组

    Object[] elementData
    public ArrayList() {
        //初始化时,赋值一个空的数组 {}
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

ii 有参int类型
//传入初始化容量

   public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

iii 有参集合类型
//记录数组实际包含的元素数量
int size;
// 支持将其他集合转为arraylist

 public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            // 判断获取的数组是否是object[]类型 
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }    
c 增加元素
//赋值并且将size+1
public boolean add(E e) {
    //确认容量是否足够
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    //先计算容量  然后确认精确的容量
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// 默认的初始化容量
int DEFAULT_CAPACITY = 10;
//对于空数组至少需要有1个容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 返回默认容量(10)和最小容量(1)的最大值
        //第一次扩容的时候,至少扩容到10
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

//记录更改的次数
int modCount = 0;
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        //扩容的核心操作
        grow(minCapacity);
}

// elementData是{}  minCapacity是10
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    // " >> 1 " 右移==/2  
    // 新容量是旧容量的1.5倍  why?
    int newCapacity = oldCapacity + (oldCapacity >> 1);

    if (newCapacity - minCapacity < 0)
        //最小值校验 当前赋值10
        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);
}

扩容算法:
i 最小扩容范围是10
ii 默认扩容范围是旧容量的1.5倍

验证性能:
如果数据量很大,并且逐个add的时候,需要扩容多少次?
1k 1w 10w 100w 分别是多少?
12 18 23 29
优秀的算法 能够减少扩容次数

3)、删除元素

支持两种方式:索引、对象本身

//以索引删除为例
public E remove(int index) {
    //范围检查
    rangeCheck(index);
    
    //更改次数+1
    modCount++;
    E oldValue = elementData(index);
    
    // 计算要移动的元素个数
    int numMoved = size - index - 1;
    if (numMoved > 0)
        //通过拷贝的方式 移动元素
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);

    //把最后一位元素置为空 size-1                     
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}
4)、遍历元素

在foreach循环中不能移除元素,会报同步修改异常
ConcurrentModificationException

fast-fail机制 (快速失败) 线程不安全
本质上,是在可能发生故障的时候,快速提醒,快速处理。

5)、线程安全(5分 知识广度)

Vector类是线程安全,过时了,加了一堆锁,效率低极了

CopyOnWriteArrayList 在concurrent包下
原理:把增删的操作通过新的复制来实现,然后改变array的引用。
(降低效率)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值