ArrayList源码学习笔记

ArrayList源码学习笔记_第1张图片
ArrayList源码学习笔记_第2张图片
在这里插入图片描述
实现了标记型接口
在这里插入图片描述

1.Serializable基本使用

ArrayList源码学习笔记_第3张图片

public class ArrayListTest {
    public static void main(String[] args) throws Exception {
        //调用写数据的方法
        writeObject();
        //调用读数据的方法
        readObject();
    }

    //定义方法将对象数据写入到文件
    private static void writeObject() throws IOException {
        //创建对象操作流-->序列化(将对象的数据写入到文件)
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\ideaPage\\SourceCode\\obj.txt"));
        //创建学生对象
        Student s1 = new Student("张三",32);
        //调用对象操作流写对象的方法,将对象的数据写入到文件中
        oos.writeObject(s1);
        //关闭流
        oos.close();
    }

    ////定义方法将文件的数据读取出来
    private static void readObject() throws IOException, ClassNotFoundException {
        //创建对象操作流-->反序列化(将数据从文件中读取出来)
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\ideaPage\\SourceCode\\obj.txt"));
        //调用方法读取一个对象
        Student stu = (Student) ois.readObject();
        //关闭流
        ois.close();
        //输出独到的对象的数据
        System.out.println(stu);
    }
}

ArrayList源码学习笔记_第4张图片

Serializable案例

public class ArrayListTest2 {
    public static void main(String[] args) throws Exception {
        Student s1 = new Student("张三",32);
        Student s2 = new Student("李四",41);
        Student s3 = new Student("王五",58);
        Student s4 = new Student("赵六",69);
        //创建ArrayList集合
        List<Student> list = new ArrayList<Student>();
        list.add(s1);
        list.add(s2);
        list.add(s3);
        list.add(s4);
        //创建对象操作流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\ideaPage\\SourceCode\\obj.txt"));
        //将集合写入到文件
        oos.writeObject(list);
        //关闭流
        oos.close();

        //反序列化
        //创建对象操作流程
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\ideaPage\\SourceCode\\obj.txt"));
        //读数据
        List<Student> list1 = (ArrayList<Student>)ois.readObject();
        //遍历集合
        for (Student student : list1) {
            System.out.println(student);
        }
    }
}

ArrayList源码学习笔记_第5张图片
toString优化
ArrayList源码学习笔记_第6张图片
ArrayList源码学习笔记_第7张图片
ArrayList源码学习笔记_第8张图片
ArrayList源码学习笔记_第9张图片
效果一样,但不会创建过多的对象,不占用过多的空间
ArrayList源码学习笔记_第10张图片

2.Cloneable基本使用

ArrayList源码学习笔记_第11张图片
ArrayList源码学习笔记_第12张图片

clone源码

ArrayList源码学习笔记_第13张图片
ArrayList源码学习笔记_第14张图片

public Object clone() {
        try {
            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元素的数组
在这里插入图片描述
ArrayList源码学习笔记_第15张图片

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        //如果条件满足,就new新数组
        //不满足就new指定类型和长度的新数组
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
         //拷贝数组的方法
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

Clone案例

1.传统方法
ArrayList源码学习笔记_第16张图片
ArrayList源码学习笔记_第17张图片
2.浅拷贝
ArrayList源码学习笔记_第18张图片
ArrayList源码学习笔记_第19张图片
ArrayList源码学习笔记_第20张图片
浅拷贝局限性
在这里插入图片描述
ArrayList源码学习笔记_第21张图片
ArrayList源码学习笔记_第22张图片
ArrayList源码学习笔记_第23张图片
ArrayList源码学习笔记_第24张图片
3.深拷贝
ArrayList源码学习笔记_第25张图片
ArrayList源码学习笔记_第26张图片
ArrayList源码学习笔记_第27张图片
ArrayList源码学习笔记_第28张图片

3.RandomAccess标记接口

ArrayList源码学习笔记_第29张图片
在这里插入图片描述
在这里插入图片描述

public class ArrayList {
    public static void main(String[] args) {
        //创建ArrayList集合
        List<String> list = new java.util.ArrayList<String>();
        //添加10W条数据
        for (int i = 0; i < 100000; i++) {
            list.add(i+"a");
        }

        //测试随机访问时间
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i++) {
            //取出集合的每一个元素
            list.get(i);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("随机访问时间"+(endTime-startTime));

        //测试顺序访问时间
        startTime = System.currentTimeMillis();
        //获取迭代器
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            //取出集合元素
            iterator.next();
        }
        endTime = System.currentTimeMillis();
        System.out.println("顺序访问时间"+(endTime-startTime));
    }
}

在这里插入图片描述
LinkedList随机和顺序访问效率对比

public class LinkedLst {
    public static void main(String[] args) {
        //创建LinkedList集合
        List<String> list = new LinkedList<String>();
        //添加10W条数据
        for (int i = 0; i < 100000; i++) {
            list.add(i+"a");
        }

        //测试随机访问时间
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i++) {
            //取出集合的每一个元素
            list.get(i);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("随机访问时间"+(endTime-startTime));

        //测试顺序访问时间
        startTime = System.currentTimeMillis();
        //获取迭代器
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            //取出集合的每一个元素
            it.next();
        }
        endTime = System.currentTimeMillis();
        System.out.println("顺序访问时间"+(endTime-startTime));
    }
}

ArrayList源码学习笔记_第30张图片
ArrayList源码学习笔记_第31张图片
案例
ArrayList源码学习笔记_第32张图片
ArrayList源码学习笔记_第33张图片
在这里插入图片描述
ArrayList源码学习笔记_第34张图片
ArrayList源码学习笔记_第35张图片

/**
 * 专门操作数据库
 */
public class EmpDao {

    //创建一个JdbcTemplate对象
    private static JdbcTemplate jt = new JdbcTemplate(JdbcUtils.getDataSource());

    //查询所有
    @Test
    public void query(){
        //拼写SQL
        String sql = "select * from user";
        List<User> list = jt.query(sql, new BeanPropertyRowMapper<User>(User.class));
        /**
         * 建议进行判断
         *  判断查询返回的结果是否实现RandomAccess接口
         *  如果实现,那么就图鉴使用 随机遍历的方式迭代集合
         *  否则,就推荐使用顺序的方式迭代集合
         */
        if (list instanceof RandomAccess){
            //随机访问
            for (int i = 0; i < list.size(); i++) {
                User user = list.get(i);
                System.out.println("随机访问"+user);
            }
        }else {
            //顺序访问
            for (User user : list) {
                System.out.println("顺序访问"+user);
            }
        }
    }
}

4.AbstractList抽象类

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
 
	public boolean add(E e) {
        add(size(), e);
        return true;
    }
    
	abstract public E get(int index);   

    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }
    
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
 
}

ArrayList源码学习笔记_第36张图片

5.ArrayList源码分析

ArrayList源码学习笔记_第37张图片
ArrayList源码学习笔记_第38张图片
ArrayList源码学习笔记_第39张图片
ArrayList源码学习笔记_第40张图片

public class ArrayList<E> {
    //空容量的数组,长度为0
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //集合真正存储数据的容器
    transient Object[] elementData;

    //集合的长度
    private int size;

    public ArrayList(Collection<? extends E> c) {
        //将构造方法中的参数转成数组
        elementData = c.toArray();

        if ((size = elementData.length) != 0) {
            //再次进行判断
            if (elementData.getClass() != Object[].class)
                //数组的创建和拷贝
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            //就把空数组的地址赋值给集合元素的数组
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

    //将集合转数组的方法
    public Object[] toArray() {
        //调用数组工具类的方法
        return Arrays.copyOf(elementData, size);
    }
}

public class Arrays {
    @SuppressWarnings("unchecked")
    public static <T> T[] copyOf(T[] original, int newLength) {
        //再次调用方法得到一个数组
        return (T[]) copyOf(original, newLength, original.getClass());
    }

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        //不管三元运算符的结果如何,都会创建一个新的数组
        //新数组的长度一定是和集合的size一样
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        //数组的拷贝
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        //返回新数组
        return copy;
    }
}

6.add方法

ArrayList源码学习笔记_第41张图片
1
ArrayList源码学习笔记_第42张图片

public class ArrayList<E> {
    //长度为0的空数组,
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //默认容量为空的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //集合真正存储数据的容器
    transient Object[] elementData;

    //集合的长度
    private int size;

    //默认的容量
    private static final int DEFAULT_CAPACITY = 10;

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
    }

    ensureCapacityInternal
    源码:
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    ensureExplicitCapacity
    //源码:
     private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    grow
    源码:
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        // >>:右移几位就相当于除以2的几次幂
        // <<:左移几位就相当于乘以2的几次幂
        //扩容的核心算法:原容量的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //元素拷贝
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
}

2
ArrayList源码学习笔记_第43张图片
ArrayList源码学习笔记_第44张图片

public class ArrayList<E> {
    //长度为0的空数组,
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //默认容量为空的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //集合真正存储数据的容器
    transient Object[] elementData;

    //集合的长度
    private int size;

    //默认的容量
    private static final int DEFAULT_CAPACITY = 10;

    public void add(int index, E element) {
        //判断index
        rangeCheckForAdd(index);
        //把modCount+1
        ensureCapacityInternal(size + 1);
        //将指定索引的原数据及后面的数据都向后移
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        //将添加的数据放到指定索引处
        elementData[index] = element;
        size++;
    }

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

    ensureCapacityInternal
    源码
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

    ensureExplicitCapacity
    源码
    private void ensureExplicitCapacity(int minCapacity) {
        //这个列表在结构上被修改的次数
        modCount++;
        //只有容量不够的情况下才会调用,核心扩容的grow方法
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
}

3
ArrayList源码学习笔记_第45张图片

public class ArrayList<E> {
    //长度为0的空数组,
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //默认容量为空的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //集合元素的数组
    Object[] elementData;

    //集合的长度
    private int size;

    //默认的容量
    private static final int DEFAULT_CAPACITY = 10;

    public boolean addAll(Collection<? extends E> c) {
        //把有数据的集合转成数组
        Object[] a = c.toArray();
        //有数据集合长度赋值给numNew
        int numNew = a.length;
        //校验以及扩容
        ensureCapacityInternal(size + numNew);  // Increments modCount
        //真正拷贝的代码
        System.arraycopy(a, 0, elementData, size, numNew);
        //集合的长度进行更改
        size += numNew;
        //根据numNew的值返回是否拷贝成功
        return numNew != 0;
    }
}

4
ArrayList源码学习笔记_第46张图片

public class ArrayList<E> {
    //长度为0的空数组,
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //默认容量为空的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //集合元素的数组
    Object[] elementData;

    //集合的长度
    private int size;

    //默认的容量
    private static final int DEFAULT_CAPACITY = 10;

    public boolean addAll(int index, Collection<? extends E> c) {
        //校验索引
        rangeCheckForAdd(index);
        //将数据源转成数组
        Object[] a = c.toArray();
        //记录数据源的长度
        int numNew = a.length;
        //目的就是为了给集合存储数据的数组进行扩容
        ensureCapacityInternal(size + numNew);  // Increments modCount
        //numMoved:代表要移动元素的个数 --> 1个
        //numMoved:数据目的(集合list1)的长度-调用addAll的第一个参数(索引1)
        int numMoved = size - index;
        //判断需要移动的个数是否大于0
        if (numMoved > 0)
            //使用System的方法arraycopy进行移动
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);
        //才是真正将数据源(list)中的所有数据添加到数据目的(集合list1)
        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }
}

5

//分析如何计算拷贝位置
public class ArrayCopyMethodTest {
    public static void main(String[] args) {
        //数据源:list
        String[] a = {"张三","李四","王五"};

        //数据目的:list1
        String[] arr = {"西游记","水浒传",null,null,null,null,null,null,null,null};
        //获取数据源的长度:3
        int numNew = a.length;
        //集合真实长度 - 要存的索引位置
        //要移动的元素的个数为:1
        int numMoved = 2 - 1;
        /**
         * 是否需要移动元素
         * src:源数组
         * srcPos:原数组中的起始位置
         * dest:目标数组
         * desyPos:目的数据中的起始位置
         * length:要复制的数组元素的数量
         */
        if (numMoved >0)
            System.arraycopy(arr,1,arr,1+numNew,numMoved);
        System.out.println(Arrays.toString(arr));
    }
}

在这里插入图片描述

7.set方法

在这里插入图片描述

/**
 * ArrayList修改方法
 * public E set(int index, E element) 根据索引修改集合元素
 */
public class Test1 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        //根据索引修改集合元素
        String value = list.set(3, "牛二");
        System.out.println("set方法返回值"+value);
        System.out.println("集合元素"+list);
    }
}

在这里插入图片描述

8.get方法

在这里插入图片描述
ArrayList源码学习笔记_第47张图片

public class ArrayList<E> {
    //长度为0的空数组,
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //默认容量为空的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //集合元素的数组
    Object[] elementData;

    //集合的长度
    private int size;

    //默认的容量
    private static final int DEFAULT_CAPACITY = 10;

    public E get(int index) {
        //校验索引
        rangeCheck(index);
        //根据索引获取数组(集合)中的元素
        return elementData(index);
    }

     private void rangeCheck(int index) {
         if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
     }

    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }
}

9.转换方法

在这里插入图片描述
ArrayList源码学习笔记_第48张图片
ArrayList源码学习笔记_第49张图片

//ArrayList集合的亲爷爷类
public abstract class AbstractCollection<E> implements Collection<E> {
    public String toString() {
        //获取迭代器
        Iterator<E> it = iterator();
        //判断迭代器是否有元素
        if (! it.hasNext())
            return "[]";
        //创建StringBuilder
        StringBuilder sb = new StringBuilder();
        //先追加了'['
        sb.append('[');
        //无限循环
        for (;;) {
            //调用迭代器的next方法取出元素,且将光标向下移动
            E e = it.next();
            //三元判断
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                //没有元素,在缓冲区的最后追加']',且把整个缓冲区的数据转成字符串
                //然后在结束该方法
                return sb.append(']').toString();
            //有元素,就直接追加
            sb.append(',').append(' ');
        }
    }
}

10.迭代器

ArrayList源码学习笔记_第50张图片
ArrayList源码学习笔记_第51张图片

public class ArrayList<E> {
    //长度为0的空数组,
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //默认容量为空的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //集合元素的数组
    Object[] elementData;

    //集合的长度
    private int size;

    //默认的容量
    private static final int DEFAULT_CAPACITY = 10;

    //获取迭代器的方法
    public Iterator<E> iterator() {
        //创建了一个对象
        return new Itr();
    }

    //ArrayList集合的内部类 --> 迭代器的源码
    private class Itr implements Iterator<E> {
        int cursor;       // 光标,默认值就是0
        int lastRet = -1; // 记录-1
        //将集合实际修改次数赋值给预期修改次数
        int expectedModCount = modCount;
        //判断集合是否有元素
        public boolean hasNext() {
            //光标是否不等于集合的size 3
            return cursor != size;
        }
        //
        @SuppressWarnings("unchecked")
        public E next() {
            //校验预期修改集合次数是否和实际修改集合次数一样
            checkForComodification();
            //光标赋值给i=0
            int i = cursor;
            //判断,如果大于集合的size就说明没有元素了
            if (i >= size)
                throw new NoSuchElementException();
            //把集合存储数据数组的地址赋值给该方法的局部变量
            Object[] elementData = ArrayList.this.elementData;
            //进行判断,如果条件满足就会产生并发修改异常
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            //光标自增
            cursor = i + 1;
            //从数组中取出元素且返回
            return (E) elementData[lastRet = i];
        }
        //校验预期修改集合次数是否和实际修改集合次数一样
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
}

并发修改异常产生的原因分析
在这里插入图片描述
ArrayList源码学习笔记_第52张图片

public class ArrayList<E> {
    //长度为0的空数组,
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //默认容量为空的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //集合元素的数组
    Object[] elementData;

    //集合的长度
    private int size;

    //默认的容量
    private static final int DEFAULT_CAPACITY = 10;

    //查看add方法其目的就是为了找到记录集合实际修改次数的变量
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
    }
     private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    //集合删除元素的方法
    public boolean remove(Object o) {
        //判断要删除的元素是否为null
        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;
    }

    //真正删除元素的方法
    private void fastRemove(int index) {
        //在删除的方法中集合实际修改次数会自增
        //集合实际修改次数:4 但是预期修改次数为:3
        modCount++;
        //计算集合要移动元素的个数
        int numMoved = size - index - 1;
        if (numMoved > 0)
            //移动的核心代码
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //就是让删除的元素置为null,就是为了尽快被垃圾回收机制回收
        elementData[--size] = null; // clear to let GC do its work
    }
}
结论:
    1.集合每次调用add方法的时候,实际修改次数变量的值都会自增一次
    2.再获取迭代器的时候,集合只会执行一次将实际修改集合的次数赋值给预期修改集合的次数
    3.集合在删除元素的时候也会针对实际修改次数的变量进行自增的操作

在这里插入图片描述
ArrayList源码学习笔记_第53张图片
ArrayList源码学习笔记_第54张图片

11.Iterator的默认方法remove

在这里插入图片描述
ArrayList源码学习笔记_第55张图片

public class ArrayList<E> {
    //长度为0的空数组,
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //默认容量为空的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //集合元素的数组
    Object[] elementData;

    //集合的长度
    private int size;

    //默认的容量
    private static final int DEFAULT_CAPACITY = 10;

    //查看add方法其目的就是为了找到记录集合实际修改次数的变量
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
    }

    //获取迭代器的方法
    public Iterator<E> iterator() {
        //创建了一个对象
        return new Itr();
    }

    //ArrayList集合的内部类 --> 迭代器的源码
    private class Itr implements Iterator<E> {
        int cursor;       // 光标,默认值就是0
        int lastRet = -1; // 记录-1
        //将集合实际修改次数赋值给预期修改次数
        int expectedModCount = modCount;
        //判断集合是否有元素
        public boolean hasNext() {
            //光标是否不等于集合的size 3
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            //校验预期修改集合次数是否和实际修改集合次数一样
            checkForComodification();
            //光标赋值给i=0
            int i = cursor;
            //判断,如果大于集合的size就说明没有元素了
            if (i >= size)
                throw new NoSuchElementException();
            //把集合存储数据数组的地址赋值给该方法的局部变量
            Object[] elementData = ArrayList.this.elementData;
            //进行判断,如果条件满足就会产生并发修改异常
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            //光标自增
            cursor = i + 1;
            //从数组中取出元素且返回
            return (E) elementData[lastRet = i];
        }
        //校验预期修改集合次数是否和实际修改集合次数一样
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

        //迭代器自带的方法
        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                //把实际修改集合次数赋值给预期修改次数
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }
    //集合删除方法
    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

        return oldValue;
    }
}
结论:
    1.迭代器调用remove方法删除元素,其实底层真正还是调用自己的删除订单来删除元素
    2.在调用remove方法中会每次都给预期修改次数的变量赋值

12.clear方法

在这里插入图片描述
ArrayList源码学习笔记_第56张图片

public class ArrayList<E> {
    //长度为0的空数组,
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //默认容量为空的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //集合元素的数组
    Object[] elementData;

    //集合的长度
    private int size;

    //默认的容量
    private static final int DEFAULT_CAPACITY = 10;

    //清空集合元素方法
    public void clear() {
        //实际修改次数自增
        modCount++;
        //遍历集合
        for (int i = 0; i < size; i++)
            //把数组的每一个位置都置为null,让垃圾回收机制尽早回收
            elementData[i] = null;
        //把集合长度置为0
        size = 0;
    }
}

13.contains方法

在这里插入图片描述

/**
 * ArrayList集合 包含方法
 *      boolean contains(Object o) 判断集合是否包含指定元素
 */
public class Test1 {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();
        //添加元素
        list.add("张三");
        list.add("李四");
        list.add("王五");

        //需求:如果集合中没有JavaSE该元素,请添加一个‘牛二’元素
        //method(list);

        //解决方法二:使用集合contains方法判断,根据判断的结果决定是否要添加元素
        if (!list.contains("牛二")){
            list.add("牛二");
        }
        System.out.println(list);
    }

    //解决方法一:循环遍历集合,判断集合是否包含‘牛二’,如果没有包含就调用集合的add方法进行添加操作
    private static void method(List<String> list) {
        //定义一个标记
        boolean flag = false;
        //遍历集合
        for (String s : list) {
            //判断
            if (s.equals("牛二")) {
                //更改标记的值
                flag = true;
                break;
            }
        }
        if (!flag) {
            list.add("牛二");
        }
        System.out.println(list);
    }
}
public class ArrayList<E> {

    //集合元素的数组
    Object[] elementData;

    //集合的长度
    private int size;

    //判断是否包含的方法
    public boolean contains(Object o) {
        //根据indexOf返回结果和0进行比较
        return indexOf(o) >= 0;
    }

    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;
        }
        //在if和else中都没有执行return,就返回-1说明没有找到此元素
        return -1;
    }
}

14.isEmpty方法

在这里插入图片描述
ArrayList源码学习笔记_第57张图片
ArrayList源码学习笔记_第58张图片

15.面试题

ArrayList源码学习笔记_第59张图片

/**
 * 面试题:
 *      ArrayList 频繁扩容导致添加性能急剧下降,如何处理
 */
public class Test1 {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();
        //添加元素
        list.add("张三");
        list.add("李四");
        list.add("王五");

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            list.add(i+"");
        }
        long endTime = System.currentTimeMillis();
        System.out.println("添加10W条数据用时"+(endTime-startTime));
        System.out.println("----------------------------------");

        List<String> list1 = new ArrayList(10000);
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            list.add(i+"");
        }
        endTime = System.currentTimeMillis();
        System.out.println("添加10W条数据用时"+(endTime-startTime));
    }
}

ArrayList源码学习笔记_第60张图片
2.
ArrayList源码学习笔记_第61张图片

/**
 * 面试题:
 *      ArrayList 频繁扩容导致添加性能急剧下降,如何处理
 */
public class Test02 {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList<String> arrayList = new ArrayList<String>();
        //添加5000w个元素
        for (int i = 0; i < 5000000; i++) {
            arrayList.add(i+"张三");
        }
        //获取开始时间
        long startTime = System.currentTimeMillis();
        //根据索引删除ArrayList集合元素
        //删除索引为50000对应的元素
        String value = arrayList.remove(50000);
        System.out.println(value);
        //获取结束时间
        long endTime = System.currentTimeMillis();
        System.out.println("ArrayList集合删除元素的时间"+(endTime-startTime));
        System.out.println("-------------------------");

        //创建LinkedList集合对象
        LinkedList<String> linkedList = new LinkedList<String>();
        //添加5000w个元素
        for (int i = 0; i < 5000000; i++) {
            linkedList.add(i+"张三");
        }
        //获取开始时间
        startTime = System.currentTimeMillis();
        //根据索引删除LinkedList集合元素
        //删除索引为50000对应的元素
        value = linkedList.remove(50000);
        System.out.println(value);
        //获取结束时间
        endTime = System.currentTimeMillis();
        System.out.println("LinkedList集合删除元素的时间"+(endTime-startTime));
    }
}

ArrayList源码学习笔记_第62张图片
ArrayList源码学习笔记_第63张图片

public class ArrayList<E> {
    public E remove(int index) {
        //范围校验
        rangeCheck(index);
        //增量++
        modCount++;
        //将index对应的元素赋值给oldValue
        E oldValue = elementData(index);
        //计算集合需要移动的元素个数
        int numMoved = size - index - 1;
        //如果需要移动的元素个数>0,就是用arraycopy方法进行拷贝
        //注意:数据源和数据目的就是elementData
        //删除元素并没有创建新的数组,只是把某个元素删除,然后arraycopy复制
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //将源集合最后一个元素置为null,尽早让垃圾回收机制对其回收
        elementData[--size] = null; // clear to let GC do its work
        //返回被删除的元素
        return oldValue;
    }
}

ArrayList源码学习笔记_第64张图片

public class LinkedList<E> {
    //LinkedList集合删除的方法
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    //校验
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }
    //到元素的方法
    Node<E> node(int index) {
        //判断index是否小于集合长度的一般
        if (index < (size >> 1)) {
            //如果小于,那么就第一个节点赋值给x
            Node<E> x = first;
            //从头开始往后找
            for (int i = 0; i < index; i++)
                //获取下一个节点
                x = x.next;
            //返回找到的节点
            return x;
        } else {
            //把最后一个节点赋值
            Node<E> x = last;
            //从最后一个位置往前找
            for (int i = size - 1; i > index; i--)
                //获取第一个节点
                x = x.prev;
            //返回找到的节点
            return x;
        }
    }
    //
    E unlink(Node<E> x) {
        //获取节点中的元素
        final E element = x.item;
        //获取下一个节点
        final Node<E> next = x.next;
        //获取上一个节点
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }
        //把元素置为空
        x.item = null;
        //长度--
        size--;
        //实际修改集合的次数自增
        modCount++;
        //返回被替换的元素
        return element;
    }
}

在这里插入图片描述
ArrayList源码学习笔记_第65张图片
ArrayList源码学习笔记_第66张图片
ArrayList源码学习笔记_第67张图片
可以加synchronized(this)
ArrayList源码学习笔记_第68张图片
或用Vector
在这里插入图片描述
或用Collections.synchronizedList()
在这里插入图片描述
ArrayList源码学习笔记_第69张图片
在这里插入图片描述
ArrayList源码学习笔记_第70张图片
ArrayList源码学习笔记_第71张图片
ArrayList源码学习笔记_第72张图片
读写分离
ArrayList源码学习笔记_第73张图片
ArrayList源码学习笔记_第74张图片
ArrayList源码学习笔记_第75张图片

16.自定义ArrayList

ArrayList源码学习笔记_第76张图片
ArrayList源码学习笔记_第77张图片
2.

/**
 * 自定义ArrayList 版本二
 *      成员变量
 *      构造方法
 *      添加方法
 *      简单扩容
 *      转换方法&测试
 *      修改&删除&测试
 *      获取方法&测试
 *          根据索引获取元素
 *          获取集合的长度
 */
public class MyArrayList<E>{
    //定义数组,用于存储集合的元素
    private Object[] elementData;
    //定义变量,用于记录数组的个数
    private int size;
    //定义空数组,用于在创建集合对象的时候给elementData初始化
    private Object[] emptyArray = {};
    //定义常量,用于记录集合的容量
    private final int DEFAULT_CAPACITY = 10;

    //构造方法
    public MyArrayList(){
        //给elementData初始化
        elementData = emptyArray;
    }

    //定义add方法
    public boolean add(E e){
        //将来在调用的时候需要判断是否需要扩容
        grow();
        //将元素添加到集合
        elementData[size++] = e;
        return true;
    }

    //简单扩容
    private void grow(){
        //判断集合存储元素的数组是否等于emptyArray
        if (elementData == emptyArray){
            //扩容
            elementData = new Object[DEFAULT_CAPACITY];
        }
        //核心算法 1.5倍
        //如果size==集合存储元素数组的长度,就需要扩容
        if (elementData.length == size){
            //先定义变量记录老容量
            int oldCapacity = elementData.length;
            //核心算法1.5倍
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            //创建一个新数组,长度就newCapacity
            Object[] obj = new Object[newCapacity];
            //拷贝元素
            System.arraycopy(elementData,0,obj,0,elementData.length);
            //把新数组的地址赋值给elementData
            elementData = obj;
        }
    }

    //转换方法
    public String toString(){
        //建议对集合进行判断,如果没有内容直接返回 "[]"
        if (size == 0){
            return "[]";
        }
        //擦混构建StringBuilder
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < size; i++) {
            if (i == size - 1){
                //追加元素,还是"]"
                sb.append(elementData[i]).append("]");
            }else {
                sb.append(elementData[i]).append(",").append(" ");
            }
        }
        //把sb中的所有数据转成一个字符串,且返回
        return sb.toString();
    }

    //修改方法
    public E set(int index,E element){
        checkIndex(index);
        //把index对应的元素取出来
        E oldValue = (E)elementData[index];
        //替换元素
        elementData[index] = element;
        return oldValue;
    }

    private void checkIndex(int index){
        if (index < 0 || index >= size){
            //制造一个异常
            throw new IndexOutOfBoundsException("索引越界了");
        }
    }

    //删除方法
    public E remove(int index){
        //校验索引
        checkIndex(index);
        //把index对应的元素取出来
        E oldValue = (E)elementData[index];
        //计算出要移动元素的个数
        int number =  size - index -1;
        if (number > 0){
            System.arraycopy(elementData,index+1,elementData,index,number);
        }
        //把最后一个元素置为null
        elementData[--size] = null;
        return oldValue;
    }

    //根据索引获取方法
    public E get(int index){
        checkIndex(index);
        //直接从elementData数组中获取元素且返回
        return (E)elementData[index];
    }

    //获取集合长度
    public int getSize(){
        return size;
    }
}

ArrayList源码学习笔记_第78张图片

你可能感兴趣的:(ArrayList源码学习笔记)