ArrayList源码

介绍

  • ArrayList非线程安全。
  • ArrayList基于动态数组,是一种线性表。随机访问友好,插入和删除效率低。

​ 增删慢:每次删除元素,都需要改变数组长度、拷贝以及移动数组长度

​ 查询快:由于数组在内存中是一块连续空间,因此可以根据地址+索引的方式快速获取对应位置上的元素。

存储的数据元素有序、可以重复

  • 容量动态调节,有一套扩容和优化空间的机制(基于数组存储数据的局限性:长度不可变,使用List替代数组

ArrayList继承了AbstractList,实现了List、RandomAccess、Cloneable、Serializable接口。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

image-20230604060711447

Serializable标记性接口

类的序列化需要实现java.io.Serializable接口,不实现此接口的类将不会使用任何状态序列化或反序列化。可序列化的所有子类都是可序列化的。序列化接口没有方法或字段,仅用于表示可串行化的语义

Cloneable标记性接口

一个类实现Cloneable接口并重写clone()方法,实现克隆

在未实现Cloneable接口的实例上调用对象的克隆方法会导致CloneNotSupportedException异常

RandomAccess标记性接口

RandomAccess接口是个空接口,作为一个标志使用,当一个类支持随机访问的时候(数组是很典型的),就可以标记这个接口。

AbstractList抽象类

ArrayList继承重写该类

常量&变量

    //序列化的VersionUID
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
     * 默认初始容量 10,注意:调用无参构造函数时,初始容量为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     * 指定初始容量为0时,返回该空数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     * 不指定初始容量时,返回该空数组。
     * 用于与EMPTY_ELEMENTDATA进行区分,在第一次添加元素时容量扩容为多少   
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     * 数组缓冲区  用于存储元素的容器,ArrayList的容量就是这个缓冲区的容量。
     * 当elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,第一次添加元素后,扩容至默认容量10。
     * 另外虽然这里用了transient修饰,但是其实现了readObject和writeObject,查看源码可知,是实现了序列化
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     * 元素个数,并不一定是容量
     * @serial
     */
    private int size;
    
    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     * 最大容量,避免在某些虚拟机下可能引起的OutOfMemoryError,
     * 减8的原因:数组作为一个对象,需要一定的内存存储对象头信息,对象头信息最大占用内存不可超过8字节
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造方法

    /**
     * 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
     * 根据给定的初始容量创建数组
     * ①指定初始容量大于0时,创建初始容量大小的数组
     * ②指定初始容量等于0时,创建一个空数组EMPTY_ELEMENTDATA
     * ③否则抛出异常IllegalArgumentException
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            //创建指定容量大小的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //创建一个空数组EMPTY_ELEMENTDATA
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                    initialCapacity);
        }
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     * 在没有指定初始容量时,创建一个空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     * 按照指定集合的迭代器返回元素的顺序,构造一个包含指定集合元素的list
     * 构建一个Object[] 赋值给数组缓冲区
     */
    public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            //传入的集合是ArrayList类型
            if (c.getClass() == ArrayList.class) {
                //直接将c的元素赋值给数组缓冲区
                elementData = a;
            } else {
                //传入的集合不是ArrayList,Arrays.copyOf复制一个Object[] 赋值给数组缓冲区
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            //传入的集合长度为0,创建一个空数组
            elementData = EMPTY_ELEMENTDATA;
        }
    }

内部类

Itr

Itr是ArrayList的普通内部类,实现了Iterator接口,通过ArrayList的itterator()方法得到的是Itr对象

作用:ArrayList实例对象的迭代器,用于元素的迭代遍历。

    /**
     * An optimized version of AbstractList.Itr
     * ArrayList的内部类
     */
    private class Itr implements Iterator<E> {
        //光标 下一个要返回的元素的索引 默认值0
        int cursor;       // index of next element to return
        //记录值 最后一个返回元素的索引,如果没有就是-1
        int lastRet = -1; // index of last element returned; -1 if no such
        //修改次数赋值给预期修改次数
        int expectedModCount = modCount;

        Itr() {}
        //是否还有下一个元素
        public boolean hasNext() {
            //判断光标是否不等于集合的size
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            //校验修改次数与预期修改次数是否一致
            checkForComodification();
            int i = cursor;
            //判断光标是否合理
            if (i >= size)
                throw new NoSuchElementException();
            //当前容纳的所有元素
            Object[] elementData = ArrayList.this.elementData;
            //再次进行下标判断,如果不一致则说明数组被修改过,就会抛出并发修改异常
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            //光标向后移动1,下个元素的下标
            cursor = i + 1;
            //取出下标对应的元素
            return (E) elementData[lastRet = i];
        }

        /**
         * 移除当前元素
         */
        public void remove() {
            //下标检查
            if (lastRet < 0)
                throw new IllegalStateException();
            //检查修改次数是否一致
            checkForComodification();

            try {
                //调用ArrayList的remove方法, 该方法会修改操作值modCount
                ArrayList.this.remove(lastRet);
                // 将下标为i的元素删除,后面的元素整体向前移动一位,i下标的元素为原来i+1下标的索引
                //故删除下标为i的元素,下一个元素的索引变为i,而cursor此时已经为i+1
                //数据修正
                cursor = lastRet;
                //防止连续删除
                lastRet = -1;
                //避免并发修改异常,同步操作值
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            //
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

ListItr

ListItr是一个具有遍历集合属性的Itr类子类,同时也是Iterator接口实现类。

ListItr的成员变量信息都继承自了父类Itr类的成员变量,ListItr并未对Itr中的hasNext()方法、next()方法、remove()方法进行覆写。覆写了ListIterator接口中的hasPrevious()、nextIndex()previousIndex()等方法。

    /**
     * An optimized version of AbstractList.ListItr
     * AbstractList.ListItr的优化版
     */
    private class ListItr extends Itr implements ListIterator<E> {
        /**
         * 有参构造
         * 由 listIterator(int index) 、 listIterator()方法创建
         * @param index
         */
        ListItr(int index) {
            super();
            cursor = index;
        }

        public boolean hasPrevious() {
            return cursor != 0;
        }

        public int nextIndex() {
            return cursor;
        }

        public int previousIndex() {
            return cursor - 1;
        }

        /**
         * 真正可以取出元素的方法,与next()含义相反,为遍历上一个
         * @return
         */
        @SuppressWarnings("unchecked")
        public E previous() {
            //判断当前数组是否发生修改
            checkForComodification();
            //游标cursor指向的上一个元素的索引
            int i = cursor - 1;
            //cursor为0的情况,即为遍历到了当前数组的第一个元素
            if (i < 0)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            //检测集合是否被修改过。
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i;
            return (E) elementData[lastRet = i];
        }

        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                //ArrayList类本身的set(int index, E element)方法对我们的元素进行修改
                ArrayList.this.set(lastRet, e);
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        public void add(E e) {
            checkForComodification();

            try {
                //插入的位置
                int i = cursor;
                //ArrayList本类的add(int index, E element)方法来进行插入操作
                ArrayList.this.add(i, e);
                cursor = i + 1;
                //避免重复操作
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }

SubList

继承了 AbstractList 类,并实现了 RandomAccess 接口,支持随机读取

SubList返回的视图是由父类集合支持的,因此是非结构化的,所以,对SubList子集合进行操作,也会修改父类的集合。SubList类中,每个public方法(除了subList()方法)都调用checkForComodification(),用于判断父类集合是否被修改。所以,如果直接使用父类方法修改集合,则SubList子类的遍历、增加、删除等操作都会抛出异常

    private class SubList extends AbstractList<E> implements RandomAccess {
        // 父类的引用
        private final AbstractList<E> parent;
        /*
         * 父类集合中的位置,如果使用SubList中的subList方法,
         * 则此时父类为SubList类,不是ArrayList
         */
        private final int parentOffset;
        // 子类List在父类 ArrayList 中的下标位置
        private final int offset;
        // 视图集合的size
        int size;

        /**
         * 构造方法,参数offset表示父类集合的下标偏移量
         * @param parent
         * @param offset
         * @param fromIndex
         * @param toIndex
         */
        SubList(AbstractList<E> parent,
                int offset, int fromIndex, int toIndex) {
            this.parent = parent;
            this.parentOffset = fromIndex;
            this.offset = offset + fromIndex;
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }

        public E set(int index, E e) {
            // 检查下标是否越界
            rangeCheck(index);
            // 检查是否有其他线程修改了父类集合
            checkForComodification();
            //获取该索引的旧值
            E oldValue = ArrayList.this.elementData(offset + index);
            // 调用父类方法替换元素,所以本质上还是在父类集合中替换元素
            ArrayList.this.elementData[offset + index] = e;
            return oldValue;
        }

        public E get(int index) {
            rangeCheck(index);
            checkForComodification();
            // 调用父类方法获取元素
            return ArrayList.this.elementData(offset + index);
        }

        public int size() {
            checkForComodification();
            return this.size;
        }

        public void add(int index, E e) {
            rangeCheckForAdd(index);
            checkForComodification();
            // 使用父类方法添加元素,
            parent.add(parentOffset + index, e);
            // 父类add()方法修改了modCount的值,更新subList的modCount值
            this.modCount = parent.modCount;
            this.size++;
        }

        /**
         * 根据下标移除元素
         * @param index the index of the element to be removed
         * @return
         */
        public E remove(int index) {
            rangeCheck(index);
            checkForComodification();
            E result = parent.remove(parentOffset + index);
            this.modCount = parent.modCount;
            this.size--;
            return result;
        }

        /**
         * 移除指定区间的元素
         * @param fromIndex index of first element to be removed
         * @param toIndex index after last element to be removed
         */
        protected void removeRange(int fromIndex, int toIndex) {
            checkForComodification();
            parent.removeRange(parentOffset + fromIndex,
                               parentOffset + toIndex);
            this.modCount = parent.modCount;
            this.size -= toIndex - fromIndex;
        }

        public boolean addAll(Collection<? extends E> c) {
            return addAll(this.size, c);
        }

        public boolean addAll(int index, Collection<? extends E> c) {
            rangeCheckForAdd(index);
            int cSize = c.size();
            if (cSize==0)
                return false;

            checkForComodification();
            parent.addAll(parentOffset + index, c);
            this.modCount = parent.modCount;
            this.size += cSize;
            return true;
        }

        /**
         *  subList 中迭代器使用ListIterator()
         * @return
         */
        public Iterator<E> iterator() {
            return listIterator();
        }

        public ListIterator<E> listIterator(final int index) {
            checkForComodification();
            rangeCheckForAdd(index);
            final int offset = this.offset;

            return new ListIterator<E>() {
                int cursor = index;
                int lastRet = -1;
                int expectedModCount = ArrayList.this.modCount;

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

                @SuppressWarnings("unchecked")
                public E next() {
                    checkForComodification();
                    int i = cursor;
                    if (i >= SubList.this.size)
                        throw new NoSuchElementException();
                    Object[] elementData = ArrayList.this.elementData;
                    if (offset + i >= elementData.length)
                        throw new ConcurrentModificationException();
                    cursor = i + 1;
                    return (E) elementData[offset + (lastRet = i)];
                }

                public boolean hasPrevious() {
                    return cursor != 0;
                }

                @SuppressWarnings("unchecked")
                public E previous() {
                    checkForComodification();
                    int i = cursor - 1;
                    if (i < 0)
                        throw new NoSuchElementException();
                    Object[] elementData = ArrayList.this.elementData;
                    if (offset + i >= elementData.length)
                        throw new ConcurrentModificationException();
                    cursor = i;
                    return (E) elementData[offset + (lastRet = i)];
                }

                @SuppressWarnings("unchecked")
                public void forEachRemaining(Consumer<? super E> consumer) {
                    Objects.requireNonNull(consumer);
                    final int size = SubList.this.size;
                    int i = cursor;
                    if (i >= size) {
                        return;
                    }
                    final Object[] elementData = ArrayList.this.elementData;
                    if (offset + i >= elementData.length) {
                        throw new ConcurrentModificationException();
                    }
                    while (i != size && modCount == expectedModCount) {
                        consumer.accept((E) elementData[offset + (i++)]);
                    }
                    // update once at end of iteration to reduce heap write traffic
                    lastRet = cursor = i;
                    checkForComodification();
                }

                public int nextIndex() {
                    return cursor;
                }

                public int previousIndex() {
                    return cursor - 1;
                }

                public void remove() {
                    if (lastRet < 0)
                        throw new IllegalStateException();
                    checkForComodification();

                    try {
                        SubList.this.remove(lastRet);
                        cursor = lastRet;
                        lastRet = -1;
                        expectedModCount = ArrayList.this.modCount;
                    } catch (IndexOutOfBoundsException ex) {
                        throw new ConcurrentModificationException();
                    }
                }

                public void set(E e) {
                    if (lastRet < 0)
                        throw new IllegalStateException();
                    checkForComodification();

                    try {
                        ArrayList.this.set(offset + lastRet, e);
                    } catch (IndexOutOfBoundsException ex) {
                        throw new ConcurrentModificationException();
                    }
                }

                public void add(E e) {
                    checkForComodification();

                    try {
                        int i = cursor;
                        SubList.this.add(i, e);
                        cursor = i + 1;
                        lastRet = -1;
                        expectedModCount = ArrayList.this.modCount;
                    } catch (IndexOutOfBoundsException ex) {
                        throw new ConcurrentModificationException();
                    }
                }

                final void checkForComodification() {
                    if (expectedModCount != ArrayList.this.modCount)
                        throw new ConcurrentModificationException();
                }
            };
        }

        public List<E> subList(int fromIndex, int toIndex) {
            subListRangeCheck(fromIndex, toIndex, size);
            return new SubList(this, offset, fromIndex, toIndex);
        }

        private void rangeCheck(int index) {
            if (index < 0 || index >= this.size)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }

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

        private String outOfBoundsMsg(int index) {
            return "Index: "+index+", Size: "+this.size;
        }

        /**
         * 检查是否有多线程修改集合
         */
        private void checkForComodification() {
            if (ArrayList.this.modCount != this.modCount)
                throw new ConcurrentModificationException();
        }

        public Spliterator<E> spliterator() {
            checkForComodification();
            return new ArrayListSpliterator<E>(ArrayList.this, offset,
                                               offset + this.size, this.modCount);
        }
    }

ArrayListSpliterator

ArrayList可分割的迭代器,基于二分法的可分割迭代器,是为了并行遍历元素而设计的一种迭代器,jdk1.8 中的集合框架中的数据结构都默认实现了 spliterator。

    /** Index-based split-by-two, lazily initialized Spliterator */
    //基于索引的、二分的、懒加载的分割器
    static final class ArrayListSpliterator<E> implements Spliterator<E> {

        /*
         * If ArrayLists were immutable, or structurally immutable (no
         * adds, removes, etc), we could implement their spliterators
         * with Arrays.spliterator. Instead we detect as much
         * interference during traversal as practical without
         * sacrificing much performance. We rely primarily on
         * modCounts. These are not guaranteed to detect concurrency
         * violations, and are sometimes overly conservative about
         * within-thread interference, but detect enough problems to
         * be worthwhile in practice. To carry this out, we (1) lazily
         * initialize fence and expectedModCount until the latest
         * point that we need to commit to the state we are checking
         * against; thus improving precision.  (This doesn't apply to
         * SubLists, that create spliterators with current non-lazy
         * values).  (2) We perform only a single
         * ConcurrentModificationException check at the end of forEach
         * (the most performance-sensitive method). When using forEach
         * (as opposed to iterators), we can normally only detect
         * interference after actions, not before. Further
         * CME-triggering checks apply to all other possible
         * violations of assumptions for example null or too-small
         * elementData array given its size(), that could only have
         * occurred due to interference.  This allows the inner loop
         * of forEach to run without any further checks, and
         * simplifies lambda-resolution. While this does entail a
         * number of checks, note that in the common case of
         * list.stream().forEach(a), no checks or other computation
         * occur anywhere other than inside forEach itself.  The other
         * less-often-used methods cannot take advantage of most of
         * these streamlinings.
         */
        //用于存放ArrayList对象
        private final ArrayList<E> list;
        //起始位置(包含),advance/split操作时会修改
        private int index; // current index, modified on advance/split
        //结束位置(不包含),-1 表示到最后一个元素
        private int fence; // -1 until used; then one past last index
        //用于存放list的modCount,当fence被设值后初始化
        private int expectedModCount; // initialized when fence set

        /** Create new spliterator covering the given  range */
        /**
         * 创建一个范围性的分割器
         * @param list
         * @param origin
         * @param fence
         * @param expectedModCount
         */
        ArrayListSpliterator(ArrayList<E> list, int origin, int fence,
                             int expectedModCount) {
            this.list = list; // OK if null unless traversed
            this.index = origin;
            this.fence = fence;
            this.expectedModCount = expectedModCount;
        }

        /**
         * 在第一次使用时实例化结束位置
         * @return
         */
        private int getFence() { // initialize fence to size on first use
            int hi; // (a specialized variant appears in method forEach)
            ArrayList<E> lst;
            if ((hi = fence) < 0) {
                if ((lst = list) == null)
                    hi = fence = 0;
                else {
                    expectedModCount = lst.modCount;
                    hi = fence = lst.size;
                }
            }
            return hi;
        }

        /**
         * 分割list,返回一个新分割出的spliterator实例,相当于二分法,这个方法会递归
         * 1.ArrayListSpliterator本质上还是对原list进行操作,只是通过index和fence来控制每次处理范围
         * 2.ArrayListSpliterator在遍历元素时,不能对list进行结构变更操作,否则抛错。
         * @return
         */
        public ArrayListSpliterator<E> trySplit() {
            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
            return (lo >= mid) ? null : // divide range in half unless too small
                new ArrayListSpliterator<E>(list, lo, index = mid,
                                            expectedModCount);
        }

        /**
         *   返回true 时,只表示可能还有元素未处理
         *   返回false 时,没有剩余元素需要处理
         * @return
         */
        public boolean tryAdvance(Consumer<? super E> action) {
            if (action == null)
                throw new NullPointerException();
            int hi = getFence(), i = index;
            if (i < hi) {
                index = i + 1;
                @SuppressWarnings("unchecked") E e = (E)list.elementData[i];
                action.accept(e);
                if (list.modCount != expectedModCount)
                    throw new ConcurrentModificationException();
                return true;
            }
            return false;
        }

        /**
         * 顺序遍历处理所有剩下的元素
         */
        public void forEachRemaining(Consumer<? super E> action) {
            int i, hi, mc; // hoist accesses and checks from loop
            ArrayList<E> lst; Object[] a;
            if (action == null)
                throw new NullPointerException();
            if ((lst = list) != null && (a = lst.elementData) != null) {
                if ((hi = fence) < 0) {
                    mc = lst.modCount;
                    hi = lst.size;
                }
                else
                    mc = expectedModCount;
                if ((i = index) >= 0 && (index = hi) <= a.length) {
                    for (; i < hi; ++i) {
                        @SuppressWarnings("unchecked") E e = (E) a[i];
                        action.accept(e);
                    }
                    if (lst.modCount == mc)
                        return;
                }
            }
            throw new ConcurrentModificationException();
        }
        //估算大小
        public long estimateSize() {
            return (long) (getFence() - index);
        }
        //获取特征值
        public int characteristics() {
            return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
        }
    }

添加

boolean add(E e)

将指定的元素追加到此列表的末尾
image-20230604060654191

    /**
     * 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})
     *
     * add添加  添加到最后。O(1)
     */
    public boolean add(E e) {
        //是否触发扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //将元素存放在size索引下,size值后自增1
        elementData[size++] = e;
        return true;
    }

void add(int index, E element)

在此列表中的指定位置插入指定的元素
image-20230604060638602

    /**
     * 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}
     * 添加到指定位置,后面依次后移。O(n)
     */
    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++;
    }

1.ensureCapacityInternal

确保容量,set后的容量是否超过上限,超过则进行扩容
image-20230604060627306

    private void ensureCapacityInternal(int minCapacity) {
        //calculateCapacity返回set后当前要存储元素的个数
        //ensureExplicitCapacity set后的要存储元素的个数和容量上限比较,是否触发扩容
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

1.1calculateCapacity

当创建ArrayList时没有设置数组长度,会创建一个空数组,等到第一次add时,才会设置数组长度为10. 此时数组存储元素的个数为max(10,minCapacity)

否则直接返回minCapacity

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //如果创建ArrayList没有指定容量大小,max取较大值max(10,minCapacity),否则返回minCapacity
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

1.2ensureExplicitCapacity

    //确保容量的方法  超过当前容量上限则进行扩容
    private void ensureExplicitCapacity(int minCapacity) {
        //用于fail-fast机制,用于在并发场景下
        modCount++;

        // overflow-conscious code  防溢出,超出容量则扩容
        if (minCapacity - elementData.length > 0)
            //扩容方法
            grow(minCapacity);
    }
1.2.1grow

扩容为原来容量的1.5倍

如果扩容后的容量依然小于要存储的个数,则数组的容量就设置为要存储的个数

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     * 私有扩容方法,确保minCapacity个数元素的存储
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        //扩容前的容量
        int oldCapacity = elementData.length;
        //扩容后的容量 为扩容前的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果扩容后的容量 依然小于要存储的个数,则数组的容量就等于存储的个数
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果扩容后的容量超出了最大数组的长度 则取integer的最大值
        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);
    }
1.2.1.1hugeCapacity
//私有 大容量分配,最大分配Integer.MAX_VALUE,最小分配MAX_ARRAY_SIZE
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }

2rangeCheckForAdd

    /**
     * A version of rangeCheck used by add and addAll.
     * 传入索引进行条件判断越界
     */
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

boolean addAll(Collection<? extends E> c)

按指定集合的iterator返回的顺序将指定集合中的所有元素追加到此列表的末尾
image-20230604060613777

/**
     * Appends all of the elements in the specified collection to the end of
     * this list, in the order that they are returned by the
     * specified collection's Iterator.  The behavior of this operation is
     * undefined if the specified collection is modified while the operation
     * is in progress.  (This implies that the behavior of this call is
     * undefined if the specified collection is this list, and this
     * list is nonempty.)
     *
     * @param c collection containing elements to be added to this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws NullPointerException if the specified collection is null
     * 将传入集合的所有元素添加到列表末尾
     */
    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        //将a数组拷贝到elementData里
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

boolean addAll(int index, Collection<? extends E> c)

将指定集合中的所有元素插入到此列表中,从指定的位置开始
image-20230604060600743

    /**
     * Inserts all of the elements in the specified collection into this
     * list, starting at the specified position.  Shifts the element
     * currently at that position (if any) and any subsequent elements to
     * the right (increases their indices).  The new elements will appear
     * in the list in the order that they are returned by the
     * specified collection's iterator.
     *
     * @param index index at which to insert the first element from the
     *              specified collection
     * @param c collection containing elements to be added to this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws NullPointerException if the specified collection is null
     * 指定位置插入集合元素
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        //需要移动的个数 = 集合真实的长度-要存储的索引位置
        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

删除

E remove(int index)

删除指定位置元素

image-20230604060548326

    /**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     *
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * 删除指定位置元素并返回 O(n)
     */
    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);
        //数组元素个数减1,将数组最后位置置空  让gc进行回收
        elementData[--size] = null; // clear to let GC do its work
        //返回旧值
        return oldValue;
    }

boolean remove(Object o)

删除指定元素

    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     * 删除给定obj
     */
    public boolean remove(Object o) {
        //传入null
        if (o == null) {
            //遍历
            for (int index = 0; index < size; index++)
                //找到第一个元素为null所在的索引
                if (elementData[index] == null) {
                    //删除元素
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                //找到第一个元素为o所在的索引
                if (o.equals(elementData[index])) {
                    //删除元素
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

void fastRemove(int index)

就是remove(int index)中删除元素的逻辑

    /*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     * 私有删除方法,不进行边界检查,不返回被删除元素
     */
    private void fastRemove(int index) {
        modCount++;
        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
    }

void removeRange(int fromIndex, int toIndex)

删除指定范围的元素

    /**
     * Removes from this list all of the elements whose index is between
     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
     * Shifts any succeeding elements to the left (reduces their index).
     * This call shortens the list by {@code (toIndex - fromIndex)} elements.
     * (If {@code toIndex==fromIndex}, this operation has no effect.)
     *
     * @throws IndexOutOfBoundsException if {@code fromIndex} or
     *         {@code toIndex} is out of range
     *         ({@code fromIndex < 0 ||
     *          fromIndex >= size() ||
     *          toIndex > size() ||
     *          toIndex < fromIndex})
     * 删除[fromIndex,toIndex)的元素
     */
    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                numMoved);

        // clear to let GC do its work
        //删除后新的元素个数
        int newSize = size - (toIndex-fromIndex);
        //删除元素
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        //更新元素个数
        size = newSize;
    }

boolean removeAll(Collection<?> c)

移除集合中的元素
image-20230604060534136

    /**
     * Removes from this list all of its elements that are contained in the
     * specified collection.
     *
     * @param c collection containing elements to be removed from this list
     * @return {@code true} if this list changed as a result of the call
     * @throws ClassCastException if the class of an element of this list
     *         is incompatible with the specified collection
     * (<a href="Collection.html#optional-restrictions">optional</a>)
     * @throws NullPointerException if this list contains a null element and the
     *         specified collection does not permit null elements
     * (<a href="Collection.html#optional-restrictions">optional</a>),
     *         or if the specified collection is null
     * @see Collection#contains(Object)
     * 移除c集合中的所有元素
     */
    public boolean removeAll(Collection<?> c) {
        //null值判断
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }

boolean retainAll(Collection<?> c)

保留集合中的元素
image-20230604060522888

    /**
     * Retains only the elements in this list that are contained in the
     * specified collection.  In other words, removes from this list all
     * of its elements that are not contained in the specified collection.
     *
     * @param c collection containing elements to be retained in this list
     * @return {@code true} if this list changed as a result of the call
     * @throws ClassCastException if the class of an element of this list
     *         is incompatible with the specified collection
     * (<a href="Collection.html#optional-restrictions">optional</a>)
     * @throws NullPointerException if this list contains a null element and the
     *         specified collection does not permit null elements
     * (<a href="Collection.html#optional-restrictions">optional</a>),
     *         or if the specified collection is null
     * @see Collection#contains(Object)
     * 保留c集合中的元素
     */
    public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }

boolean batchRemove(Collection c, boolean complement)

removeAll方法和retainAll方法都调用了batchRemove方法,区别就在于参数complement

如果为true只保留c集合中元素,如果false,移除c集合中的元素

以removeAll为例,当complement为false

    /**
     * 批量移除。O(n)
     * @param c
     * @param complement   如果为true只保留c集合中元素,如果false,移除c集合中的元素
     * @return
     */
    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        //两个指针,r是读取位置,w是写入位置
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                //遍历数组,当c中不存在该元素时,边判定边写入元素
                if (c.contains(elementData[r]) == complement)
                    //elementData[r] 遍历的元素
                    //elementData[w++] 写入的元素 写入后指针+1
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            //如果读的元素个数 不等于 数组的个数 说明在操作中抛出了异常,出错后保证数据的完整性
            if (r != size) {
                //数组拷贝 从出错的位置开始,将后面所有元素拷贝到写入元素的后面
                System.arraycopy(elementData, r,
                        elementData, w,
                        size - r);
                //写入的个数 = 真正写入的个数 + 移动元素的个数
                w += size - r;
            }
            //说明写入的个数小于元素的个数,有元素需要删除
            if (w != size) {
                // clear to let GC do its work
                //此时索引w 至 size-1的元素都是要删除的
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                //元素的个数为w
                size = w;
                //修改成功
                modified = true;
            }
        }
        return modified;
    }

边遍历边赋值

比如原数组:1 2 3 4 5 6 7 8 9;参数数组:a b c 3 5 8 d

循环次数rwif值elementData[w++] = elementData[r]原数组值替换值
100trueelementData[0] = elementData[0]123456789123456789
211trueelementData[1] = elementData[1]123456789123456789
322flase
432trueelementData[2] = elementData[3]123456789124456789
543flase
653tureelementData[3] = elementData[5]124456789124656789
764trueelementData[4] = elementData[6]124656789124676789
875flase
985trueelementData[5] = elementData[8]124676789124679789

w为5,将数组索引6,7,8元素删除,即将7,8,9删除

原数组变为1 2 4 6 7 9

    public static void main(String[] args) {
        ArrayList<Object> src = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
        ArrayList<Object> c = new ArrayList<>(Arrays.asList("a", "b", "c", 3, 5, 8, "d"));
        System.out.println(src.removeAll(c));
        System.out.println(src);
    }

输出结果

image-20230604060447403

void clear()

清空数组

    /**
     * Removes all of the elements from this list.  The list will
     * be empty after this call returns.
     * 清空list,释放空间 O(n)
     */
    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

查找

E get(int index)

    /**
     * Returns the element at the specified position in this list.
     *
     * @param  index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * 获取指定位置元素
     */
    public E get(int index) {
        //检查索引是否在条件范围内
        rangeCheck(index);
        //返回该索引对应的元素
        return elementData(index);
    }

int indexOf(Object o)

顺序查找,返回obj所在下标

    /**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the lowest index <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     * 顺序查找,返回首先出现的位置,找不到返-1。O(n)
     */
    public int indexOf(Object o) {
        //ArrayList可以存储null  查询的元素为null的情况
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
            //不为null的情况
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

int lastIndexOf(Object o)

逆序查找,返回obj所在下标

    /**
     * Returns the index of the last occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the highest index <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     * 逆序查找,返回最后出现的位置,找不到返-1。O(n)
     */
    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

boolean contains(Object o)

判断集合是否包含指定元素

    /**
     * Returns <tt>true</tt> if this list contains the specified element.
     * More formally, returns <tt>true</tt> if and only if this list contains
     * at least one element <tt>e</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
     *
     * @param o element whose presence in this list is to be tested
     * @return <tt>true</tt> if this list contains the specified element
     * 顺序查找实现,根据返回值判断集合是否包含元素o
     */
    public boolean contains(Object o) {
        //indexOf(o) >= 0 表示查找到了存储的o元素
        return indexOf(o) >= 0;
    }

修改

E set(int index, E element)

    /**
     * Replaces the element at the specified position in this list with
     * the specified element.
     *
     * @param index index of the element to replace
     * @param element element to be stored at the specified position
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * 修改指定位置元素
     */
    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        //赋值
        elementData[index] = element;
        //返回旧值
        return oldValue;
    }

迭代器

Iterator iterator()

创建一个Iterator的子类Itr

    /**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
     *
     * @return an iterator over the elements in this list in proper sequence
     */
    public Iterator<E> iterator() {
        //内部类Itr
        return new Itr();
    }

其他

List subList(int fromIndex, int toIndex)

返回SubList类型的子集合
image-20230604060433679

    /**
     * Returns a view of the portion of this list between the specified
     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.  (If
     * {@code fromIndex} and {@code toIndex} are equal, the returned list is
     * empty.)  The returned list is backed by this list, so non-structural
     * changes in the returned list are reflected in this list, and vice-versa.
     * The returned list supports all of the optional list operations.
     *
     * <p>This method eliminates the need for explicit range operations (of
     * the sort that commonly exist for arrays).  Any operation that expects
     * a list can be used as a range operation by passing a subList view
     * instead of a whole list.  For example, the following idiom
     * removes a range of elements from a list:
     * <pre>
     *      list.subList(from, to).clear();
     * </pre>
     * Similar idioms may be constructed for {@link #indexOf(Object)} and
     * {@link #lastIndexOf(Object)}, and all of the algorithms in the
     * {@link Collections} class can be applied to a subList.
     *
     * <p>The semantics of the list returned by this method become undefined if
     * the backing list (i.e., this list) is <i>structurally modified</i> in
     * any way other than via the returned list.  (Structural modifications are
     * those that change the size of this list, or otherwise perturb it in such
     * a fashion that iterations in progress may yield incorrect results.)
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws IllegalArgumentException {@inheritDoc}
     * 取子list,返回Sublist这个ArrayList的内部类,
     * 这是个坑,注意SubList和其他List实现类的区别
     */
    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }

    /**
     * 越界判断
     * @param fromIndex
     * @param toIndex
     * @param size
     */
    static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > size)
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
    }

void sort(Comparator<? super E> c)

    /**
     * 传入Compartor,用Arrays.sort()实现,主要是LegacyMergeSort和Timsort
     * @param c the {@code Comparator} used to compare list elements.
     *          A {@code null} value indicates that the elements'
     *          {@linkplain Comparable natural ordering} should be used
     */
    @Override
    @SuppressWarnings("unchecked")
    public void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        //实现排序的方法
        Arrays.sort((E[]) elementData, 0, size, c);
        //排序完,再次判断,防止并发修改
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

boolean isEmpty()

判断集合是否为空

    /**
     * Returns <tt>true</tt> if this list contains no elements.
     *
     * @return <tt>true</tt> if this list contains no elements
     * 判空,直接看size就行了
     */
    public boolean isEmpty() {
        return size == 0;
    }

Object clone()

克隆拷贝

    /**
     * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
     * elements themselves are not copied.)
     *
     * @return a clone of this <tt>ArrayList</tt> instance
     * 克隆,主要拷贝elementData数组  ArrayLis的clone方法返回类型为ArrayList的类型
     */
    public Object clone() {
        try {
            //调用父类Object的clone方法,强转为ArrayList
            ArrayList<?> v = (ArrayList<?>) super.clone();
            //将原集合的元素赋值给新的集合,指定新集合的长度
            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);
        }
    }

void trimToSize()

    /**
     * Trims the capacity of this <tt>ArrayList</tt> instance to be the
     * list's current size.  An application can use this operation to minimize
     * the storage of an <tt>ArrayList</tt> instance.
     * 拷贝到新数组中,释放多余空间
     */
    public void trimToSize() {
        modCount++;
        //存储元素小于数组的长度
        if (size < elementData.length) {
            elementData = (size == 0)
                    //空数组
                    ? EMPTY_ELEMENTDATA
                    //拷贝
                    : Arrays.copyOf(elementData, size);
        }
    }

String toString()

ArrayList本身是没有toString方法,调用其父类AbstractCollection的toString方法

AbstractCollection.java

AbstractCollection.java
     
    /**
     * Returns a string representation of this collection.  The string
     * representation consists of a list of the collection's elements in the
     * order they are returned by its iterator, enclosed in square brackets
     * (<tt>"[]"</tt>).  Adjacent elements are separated by the characters
     * <tt>", "</tt> (comma and space).  Elements are converted to strings as
     * by {@link String#valueOf(Object)}.
     *
     * @return a string representation of this collection
     */
    public String toString() {
        //获取迭代器
        Iterator<E> it = iterator();
        //判断迭代器是否有元素
        if (! it.hasNext())
            return "[]";
        //字符串拼接
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        //无限循环
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            //已经没有元素了,返回拼接的字符串
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }

答疑

1 ArrayList频繁扩容导致添加性能急剧下降,该如何处理

ArrayList(int initialCapacity) 构造具有指定初始容量的空列表。可以根据业务场景创建一个适当初始容量的集合,避免了扩容的性能影响。

2ArrayList插入或删除元素一定比LinkedList慢吗?

3ArrayList在什么情况下要保证同步

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/26907.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

中国计算机学会CCF推荐的国际会议(图像处理方向)

CCF推荐的国际会议&#xff08;医学图像处理方向&#xff09; 1 介绍2 最新目录3 投了会议可以再投期刊吗&#xff1f;4 个人感想 1 介绍 CCF根据论文的质量和影响力&#xff0c;对国际期刊和国际会议进行了评估和分类&#xff0c;以便研究者在选择发表论文或参与学术交流时有…

94.构建样品餐部分第二节

上节课完成的页面是这样的 ● 之后我们设置一下图标 .meal-attribute {font-size: 1.8rem;font-weight: 500;display: flex;align-items: center;gap: 1.6rem; }.meal-img {width: 100%; }.meal-icon {height: 2.4rem;width: 2.4rem;color: #e67e22; }● 为了突出这些参数的…

CDH 之 hive 升级至 hive-3.1.3 完美踩坑过程

一、准备工作 1.1 前言 这是博主在升级过程中遇到的问题记录&#xff0c;大家不一定遇到过&#xff0c;如果不是 CDH 平台的话&#xff0c;单是 hive 服务升级应该是不会有这些问题的&#xff0c;且升级前博主也参考过几篇相关 CDH 升级 hive 服务的博文&#xff0c;前面的升级…

Java实现LL1语法分析器【编译原理】

java通过预测分析法实现语法分析程序【编译原理】 前言推荐实验要求需知LL1工作原理 Java实现LL1语法分析器0实验步骤LL1.javaGrammar.javaLeftRecursion.javaFirstAndFollow.javaAnalyzeTable.javaLL1Stack.java实验结果 Java实现LL1语法分析器1Grammar.javaProduction.javaFi…

Neo4j安装配置教程

目录结构 前言Neoj4简介安装JDKNeo4j安装步骤Neo4j下载解压Neo4j压缩包配置环境变量启动Neo4j执行命令报错&#xff0c;报错信息如下&#xff1a; 启动Neo4j&#xff0c;再次测试浏览器访问Neo4j参考链接 前言 安装所需配件网盘一键下载。以下描述中&#xff0c;官网下载均有描…

5.3图的综合应用算法

一.最小生成树算法 1.概念&#xff08;Minimum-Spanning-Tree&#xff09;MST 生成树&#xff1a;针对于连通图&#xff0c;包含全部顶点&#xff0c;去掉一条边后不连通&#xff0c;加一条边形成环 最小生成树:带权连通无向图&#xff0c;边的权值之和最小的生成树(MST) 2.…

基于LPP算法实现MNIST数据集降维

目录 1、作者介绍2、LPP算法简介2.1 基本概念及原理2.2 算法流程 3、LPP算法实现3.1 数据集简介3.2 代码实现3.2.1 完整代码3.2.2 运行结果 4、参考链接 1、作者介绍 刘晨雨&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2022级研究生 研究方向&#xff1a;…

Tomcat部署

Tomcat 一、Tomcat什么是 servlet&#xff1f;什么是 JSP?Tomcat 功能组件结构&#xff1a;Container 结构分析&#xff1a;Tomcat 请求过程&#xff1a; 二、Tomcat服务部署三、虚拟主机配置四、Tomcat多实例部署 Tomcat 是 Java 语言开发的&#xff0c;Tomcat 服务器是一个免…

前端录制回放rrweb

rrweb 是 ‘record and replay the web’ 的简写&#xff0c;旨在利用现代浏览器所提供的强大 API 录制并回放任意 web 界面中的用户操作。 rrweb中文文档 https://github.com/rrweb-io/rrweb/blob/master/guide.zh_CN.md 本文项目地址 https://github.com/qdfudimo/vue-rrweb…

目标检测算法:YOLO v1论文解读

目标检测算法&#xff1a;YOLO v1论文解读 前言 ​ 其实网上已经有很多很好的解读各种论文的文章了&#xff0c;但是我决定自己也写一写&#xff0c;当然&#xff0c;我的主要目的就是帮助自己梳理、深入理解论文&#xff0c;因为写文章&#xff0c;你必须把你所写的东西表达清…

深入了解Java虚拟机之高效并发

目录 Java内存模型与线程 概述 硬件的效率与一致性 Java内存模型 主内存与工作内存 内存间交互操作 对于volatile型变量的特殊规则 原子性、可见性与有序性 先行发生原则 Java与线程 线程实现 线程调度 状态切换 小结 线程安全与锁优化 概述 线程安全 Java中…

简单的UDP网络程序·续写

该文承接文章 简单的UDP网络程序 对于客户端和服务端的基本源码参考上文&#xff0c;该文对服务器润色一下&#xff0c;并且实现几个基本的业务服务逻辑 目录 demo1 第一个功能&#xff1a;字典翻译 初始化字典 测试代码&#xff1a;打印 字符串分割 客户端修改 成品效果…

AI宝典:AI超强工具大整合

&#x1f604;&#x1f604;个人介绍 光子郎.进行开发工作七年以上&#xff0c;目前涉及全栈领域并进行开发。会经常跟小伙伴分享前沿技术知识&#xff0c;java后台、web前端、移动端&#xff08;Android&#xff0c;uniapp&#xff0c;小程序&#xff09;相关的知识以及经验体…

C#发送邮箱设置及源码

用C#调用发送邮箱代码之前需要邮箱开通SMTP/POP3及设置授权码&#xff0c;开通及获取方法如下&#xff1a; 1、打开邮箱&#xff0c;登录邮箱&#xff0c;进入设置&#xff0d;》帐户 2、在“帐户”设置中&#xff0c;找到服务设置项&#xff0c;进行设置&#xff0c;如下…

[Flink] Flink On Yarn(yarn-session.sh)启动错误

在Flink上启动 yarn-session.sh时出现 The number of requested virtual cores for application master 1 exceeds the maximum number of virtual cores 0 available in the Yarn Cluster.错误。 版本说明&#xff1a; Hadoop&#xff1a; 3.3.4 Flink&#xff1a;1.17.1 问题…

Python实战基础20-解密文件及目录操作

任务1 为泸州驰援湖北的89名白衣勇士点赞 【任务描述】 设计python程序&#xff0c;实现用户可以为泸州驰援湖北的89名白衣勇士点赞留言。用户点赞留言内容保存到本地txt文件中。 import os # 导入os模块 import random # 导入随机模块 import string # 导入string模块# 定义…

《Lua程序设计》--学习3

输入输出 简单I/O模型 Lua 文件 I/O | 菜鸟教程 (runoob.com) 暂留 补充知识 局部变量和代码块 Lua语言中的变量在默认情况下是全局变量&#xff0c;所有的局部变量在使用前必须声明 在交互模式中&#xff0c;每一行代码就是一个代码段&#xff08;除非不是一条完整的命…

chatgpt赋能python:Python如何将IP地址转换为整数

Python如何将IP地址转换为整数 在计算机网络中&#xff0c;IP地址是一个包含32位的二进制数字&#xff0c;通常由四个8位二进制数字&#xff08;即“点分十进制”&#xff09;表示。但在某些情况下&#xff0c;需要将IP地址转换为整数&#xff0c;例如在网络编程中检查网络连接…

Ingress详解

Ingress Service对集群外暴露端口两种方式&#xff0c;这两种方式都有一定的缺点&#xff1a; NodePort &#xff1a;会占用集群集群端口&#xff0c;当集群服务变多时&#xff0c;缺点明显LoadBalancer&#xff1a;每个Service都需要一个LB&#xff0c;并且需要k8s之外设备支…

FPGA量子类比机制-FPQA,将在量子运算设计中引发一场新的革命

1980年代现场可程式化逻辑门阵列(FPGA)的出现彻底改变了电子设计。大约40年后&#xff0c;现场可程式化量子位元阵列(FPQA)可望在量子运算电路设计中引发一场类似的革命。 1980年代现场可程式化逻辑闸阵列(FPGA)的出现彻底改变了电子设计。FPGA允许设计人员创建适合特定应用的…