集合篇之ArraryList

1.ArraryList是什么?

  • ArrayList就是数组列表,主要用来装载数据,当我们装载的是基本类型的数据int,long,boolean,short,byte…的时候我们只能存储他们对应的包装类,它的主要底层实现是数组Object[] elementData。
  • 与它类似的是LinkedList,和LinkedList相比,它的查找和访问元素的速度较快,但插入,删除的速度较慢。
  • ArrayList是线程不安全的,会出问题的部分在于: 1. 增加元素 2. 扩充数组长度;
  • 小结:ArrayList底层是用数组实现的存储。查询效率高,增删效率低,线程不安全。

2.ArraryList的大小与扩容

  • 有参构造方法(通过构造方法在初始化的时候指定底层数组的大小,实际上并没有初始化数组大小)
/**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    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);
        }
    }
  • 无参构造方法(赋值底层数组Object[] elementData为一个默认空数组Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}所以数组容量为0,只有真正对数据进行添加add时,才分配默认DEFAULT_CAPACITY = 10的初始容量。)
/**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
/**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);//这里设置默认容量值
        }
        return minCapacity;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

数组扩容后长度为原来的1.5倍

/**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            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);
    }

3.为什么说ArraryList插入删除慢

  • 插入位置为n时,会复制数组n后面的数据放到n+1后面,然将插入的元素放到n的位置上。所以当数据量特别大时候,效率就会底下。
/**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);//这里进行复制
        elementData[index] = element;
        size++;
    }

例子:在5的位置插入9

12345678

会把从5开始的数据复制为一个数组放到6的位置上

12345678

将9插入到5的位置上

123495678
  • 删除同理,要删除这个数组中的5这个位置,复制一个从6开始到最后的数组,然后把它放到5的位置,5的位置就成功被”删除“了其实就是被覆盖了,给了你被删除的感觉。同理他的效率也低,因为数组如果很大的话,需要复制和移动的位置就大了

4.关于ArrayList的那些事儿

  1. 都知道ArrayList在foreach中不能对集合进行增加删除操作,否则会抛出异常。
  2. 在遍历过程中使用一个 modCount 变量(表示修改次数)。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedModCount值(在迭代器初始化过程中会将modCount这个值赋给迭代器的expectedModCount),是的话就返回遍历;否则抛出异常,终止遍历。(这就是快速失败机制)
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
  1. 一个有趣的现象,执行下面代码,程序并不会抛出异常(很神奇吧),这是为什么呢?
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        for (Integer integer : list) {
            if(integer.equals(1)){
                list.remove(integer);
            }
        }

这是判断是否进入循环 ,cursor代表当前下标,size代表集合长度

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

这是因为当元素只有两个,且删除第一个元素后,导致cursor和size刚好相同,循环结束,也就没有异常抛出。

  1. 循环删除的方法
  • 方法1(用fori正序,注意某些情况下会删除漏,只适合删除一个元素的情况)
  • 方法2 (fori倒序)
  • 方法3 Iterator迭代器删除
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            if (iterator.next().equals(2)){
                iterator.remove();
            }
        }
  • 方法4 用removeif(jdk1.8 Collection以及其子类新加入了removeIf方法)
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.removeIf(s->s.equals(1));
  1. 数组转list的方法
  • 方法1(注意,此方法转换后的list不能进行增删操作,因为实际返回的ArrayList是Arrays的一个静态内部类,该类没有提供增删方法)
        Integer[] a =new Integer[]{1,2};
        List<Integer> list = Arrays.asList(a);
  • 方法2 (支持增删,适用于数据小)
        Integer[] a =new Integer[]{1,2};
        List<Integer> list = new ArrayList<>(Arrays.asList(a));
  • 方法3(最高效)
        Integer[] a =new Integer[]{1,2};
        List<Integer> list = new ArrayList<>(a.length);
        Collections.addAll(list,a);

ArraryList常用方法

  • boolean add(E e)

将指定的元素添加到此列表的尾部。

  • void add(int index, E element)

将指定的元素插入此列表中的指定位置。

  • boolean addAll(Collection c)

按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。

  • boolean addAll(int index, Collection c)

从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。

  • void clear()

移除此列表中的所有元素。

  • Object clone()

返回此 ArrayList 实例的浅表副本。

  • boolean contains(Object o)

如果此列表中包含指定的元素,则返回 true。

  • void ensureCapacity(int minCapacity)

如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。

  • E get(int index)

返回此列表中指定位置上的元素。

  • int indexOf(Object o)

返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。

  • boolean isEmpty()

如果此列表中没有元素,则返回 true

  • int lastIndexOf(Object o)

返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。

  • E remove(int index)

移除此列表中指定位置上的元素。

  • boolean remove(Object o)

移除此列表中首次出现的指定元素(如果存在)。

  • protected void removeRange(int fromIndex, int toIndex)

移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。

  • E set(int index, E element)

用指定的元素替代此列表中指定位置上的元素。

  • int size()

返回此列表中的元素数。

  • Object[] toArray()

按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。

  • T[] toArray(T[] a)

按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。

  • void trimToSize()

将此 ArrayList 实例的容量调整为列表的当前大小。

  • boolean removeIf(Predicate<? super E> filter)

按照一定规则过滤集合中的元素

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值