欢迎访问宙启技术站
智能推送

Java中Arraylist的函数实现原理详解

发布时间:2023-06-08 07:16:59

ArrayList是Java语言中的一种动态数组,在实现原理上有一些非常重要的细节需要注意。本文将从数组存储原理、扩容机制、迭代器实现、线程安全性等方面进行详细解析。

1. 数组存储原理

ArrayList是通过数组对数据进行存储的,用数组存储数据的好处是可以通过下标访问元素,因此对于随机访问的操作效率比较高。在ArrayList中,数组由elementData字段进行维护,其初始大小为10。

2. 扩容机制

当数组中的元素个数达到数组容量时,需要进行扩容。在ArrayList中,扩容的机制是在添加元素时进行的。当添加一个元素时,首先判断数组是否已满。如果数组已满,则调用grow方法进行扩容。

    private void grow(int minCapacity) {

        int oldCapacity = elementData.length;

        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);

    }

在grow方法中,首先获取当前数组的容量 oldCapacity,然后通过 oldCapacity >> 1 计算出扩容后的新容量 newCapacity。其中,>> 1 表示将 oldCapacity 右移一位,相当于 oldCapacity 除以 2 的结果。如果新容量小于添加元素所需的容量 minCapacity,则使用 minCapacity 值作为新容量。另外,如果新容量超过了数组的最大容量 MAX_ARRAY_SIZE,则调用 hugeCapacity 方法进行处理。

    private static int hugeCapacity(int minCapacity) {

        if (minCapacity < 0) 

            throw new OutOfMemoryError();

        return (minCapacity > MAX_ARRAY_SIZE) ?

            Integer.MAX_VALUE :

            MAX_ARRAY_SIZE;

    }

在 hugeCapacity 方法中,首先判断 minCapacity 是否小于0,如果小于0则抛出 OutOfMemoryError。如果 minCapacity 大于 MAX_ARRAY_SIZE,则将容量设置为 Integer.MAX_VALUE(即数组长度的最大值),否则设为 MAX_ARRAY_SIZE。

最后,在扩容完成后,将原数组中的元素复制到新数组中。

3. 迭代器实现

在对ArrayList进行迭代时,Java提供了Iterator接口供我们使用。其中,Arraylist的迭代器实现是通过内部类Itr来实现的,代码如下:

    private class Itr implements Iterator<E> {

        int cursor;       // 下一个元素的下标

        int lastRet = -1; // 最后一个返回的元素的下标

        int expectedModCount = modCount;

        public boolean hasNext() {

            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();

            cursor = i + 1;

            return (E) elementData[lastRet = i];

        }

        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();

            }

        }

        // 当数组在迭代过程中被修改时,抛出ConcurrentModificationException异常

        final void checkForComodification() {

            if (modCount != expectedModCount)

                throw new ConcurrentModificationException();

        }

    }

在Itr中,包含了当前元素的下标 cursor、上一个元素的下标 lastRet 和集合的修改次数 modCount 等字段。在hasNext方法中,判断 cursor 是否等于 size,如果相等就代表已经迭代到了最后一个元素,没有其它元素可以迭代,因此返回false。在next方法中,首先调用checkForComodification方法判断是否发生过ConcurrentModificationException异常,然后获取当前元素的下标 i,如果 i 大于等于 size,则抛出NoSuchElementException异常,如果 i 大于等于数组长度 elementData.length,则说明在迭代过程中发生了ConcurrentModificationException异常,因为数组的容量已经发生了变化。最后,将 cursor 的值加1,返回当前元素值。在remove方法中,首先检查lastRet的值,如果小于0则抛出IllegalStateException异常;然后调用checkForComodification方法检查是否发生过ConcurrentModificationException异常;最后,调用ArrayList的remove方法删除元素。

4. 线程安全性

ArrayList不是线程安全的,不建议在多线程环境下直接使用。如果需要在多线程环境下使用,需要采取一些线程安全措施,如使用Collections.synchronizedList方法将ArrayList转换为线程安全的List,或使用CopyOnWriteArrayList类代替ArrayList。

综上所述,ArrayList通过数组对数据进行存储,通过扩容机制进行容量管理,迭代器实现对数组元素进行遍历,因为非线程安全,所以在多线程环境下需要采取线程安全措施。对于ArrayList的理解和应用可以帮助我们更好的掌握Java语法和数据结构。