3. ArrayList、Iterator以及LinkedList

1. ArrayList

ArrayList扩容机制

扩容规则

  1. ArrayList() 会使用长度为零的数组

  2. ArrayList(int initialCapacity) 会使用指定容量的数组

  3. public ArrayList(Collection<? extends E> c) 会使用 c 的大小作为数组容量

  4. add(Object o) 首次扩容为 10,再次扩容为上次容量的 1.5 倍

  5. addAll(Collection c) 没有元素时,扩容为 Math.max(10, 实际元素个数),有元素时为 Math.max(原容量 1.5 倍, 实际元素个数)

2. Iterator

Fail-Fast 与 Fail-Safe

  • ArrayList 是 fail-fast 的典型代表,遍历的同时不能修改,尽快失败

  • CopyOnWriteArrayList 是 fail-safe 的典型代表,遍历的同时可以修改,原理是读写分离

2.1 Fail-Fast源码分析
 private static void failFast() {
        ArrayList<Student> list = new ArrayList<>();
        list.add(new Student("A"));
        list.add(new Student("B"));
        list.add(new Student("C"));
        list.add(new Student("D"));
        
     for (Student student : list) {
            System.out.println(student);
     }
       
     System.out.println(list);
    }

当使用增强for遍历Arraylist集合时,底层是使用迭代器遍历集合的,创建迭代器时把集合最开始被修改的次数记录下来,赋值给expectedModCount变量,每次调用next方法时都会检查expectedModCount变量的值是否与集合最开始被修改的次数是否一致,如果不一致,证明在遍历集合的同时被修改了,抛出并发修改异常

    public Iterator<E> iterator() {
        return new Itr();
    }


    private class Itr implements Iterator<E> {
        int cursor;       // 返回下一个元素索引
        int lastRet = -1; // 返回最后一个元素索引,不存在则返回-1
        int expectedModCount = modCount;//把最开始集合被修改的次数赋值给expectedModCount变量。例如集合调用了四次add方法,则集合在遍历之前被修改了四次

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

       
        public E next() {
            checkForComodification();
           ......
        }
         
        final void checkForComodification() {
            if (modCount != expectedModCount)//
                throw new ConcurrentModificationException();
        }
2.3 Fail-Safe源码分析
 private static void failSafe() {
        CopyOnWriteArrayList<Student> list = new CopyOnWriteArrayList<>();
        list.add(new Student("A"));
        list.add(new Student("B"));
        list.add(new Student("C"));
        list.add(new Student("D"));
        for (Student student : list) {
            System.out.println(student);
        }
        System.out.println(list);
    }

使用增强for遍历的时候,创建迭代器对象,在迭代器内部记录被遍历的数组,而在添加元素时拷贝旧数组得到一个新数组,操作的是另一个数组,这样的现象就是,遍历的时候是遍历一个数组,添加的时候是操作另外一个数组,实现了读写分离

    /** Snapshot of the array */
    private final Object[] snapshot;//记录被遍历的数组
    
    /** Index of element to be returned by subsequent call to next.  */
    private int cursor;


    public Iterator<E> iterator() {
        return new COWIterator<E>(getArray(), 0);//参数是被遍历的数组
    }


    private COWIterator(Object[] elements, int initialCursor) {
            snapshot = elements;//elements当前遍历的数组,赋值给snapshot
            cursor = initialCursor;
    }


    public boolean hasNext() {
            return cursor < snapshot.length;
    }

     public E next() {
            if (! hasNext())
                throw new NoSuchElementException();
            return (E) snapshot[cursor++];
        }

而CopyOnWriteArrayList调用add方法添加元素时,复制旧数组,获取一个长度比旧数组长度加一的新数组

,添加元素时填加到了新数组中

    public boolean add(E e) {
          final ReentrantLock lock = this.lock;
          lock.lock();
          try {
              //1.获取原来旧的数组
              Object[] elements = getArray();
              //2.旧数组的长度
              int len = elements.length;
              //3.拷贝数组,得到一个新的数组,新数组长度比原来数组长度加一
              Object[] newElements = Arrays.copyOf(elements, len + 1);
              //4.把正在调用add方法添加的元素添加到新数组的
              newElements[len] = e;
              
              setArray(newElements);
              
              return true;
          } finally {
              lock.unlock();
          }
      }

3. LinkedList 对比 ArrayList 的区别

LinkedList

  1. 底层数据结构基于双向链表,无需连续内存
  2. 随机访问慢(要沿着链表遍历)
  3. 头尾插入和删除的性能高
  4. 占用内存多

ArrayList

  1. 底层数据结构基于数组,需要连续内存
  2. 随机访问快(指根据下标访问)
  3. 尾部插入、删除性能可以,其它部分插入、删除都会移动数据,因此性能会低
  4. 可以利用 cpu 缓存,局部性原理
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

糖分你俩颗~~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值