《算法与数据结构:手写 ArrayList》 List 的数组 List(列表)是编程中一种极为常见且重要的数据结构,用于存储元素的有序集合。在 Java 中,List 是一个接口,它允许我们以线性方式存储元素,同时提供了一系列操作这些元素的方法,List 是线性表的一种。
线性表是一种最基本、最简单、也是最常用的一种数据结构。一个线性表是 n 个具有相同特性的数据元素的有限序列。在这些元素中,每个元素都有一个前驱和一个后继,除了第一个和最后一个元素之外,其它元素都是首尾相接的。
List 数据结构的基本概念 定义 :List 是一种允许重复元素的序列,它可以维护元素插入的顺序。特点: 动态大小: List 的大小不是固定的,它可以根据需要增长或缩小。有序集合: 每个元素都有其特定的位置(索引)。List 的重要性 灵活性 :List 比数组更加灵活,可以轻松地添加、删除和访问元素。应用广泛 :从简单的客户端应用程序到复杂的企业级解决方案,List 在各种场景下都非常有用。List 的常见操作 添加元素 :可以在 List 的任意位置添加元素。删除元素 :可以删除指定位置或指定值的元素。查找元素 :可以搜索 List 中的元素,并返回其位置。遍历元素 :可以遍历 List 中的所有元素。List 抽象接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 public interface List <E> { E get (int index) ; E set (int index, E e) ; void add (int index, E e) ; boolean add (E e) ; int indexOf (E e) ; int lastIndexOf (E o) ; boolean contains (E o) ; E remove (int index) ; boolean remove (E o) ; int size () ; boolean empty () ; }
数组实现 List 的数据结构基础
默认容量 DEFAULT_CAPACITY 可共享的空实例数组 EMPTY_ELEMENTDATA 真正存放数据的数组 elementData 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private static final int DEFAULT_CAPACITY = 10 ;private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 ;private static final Object[] EMPTY_ELEMENTDATA = {};private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};transient Object[] elementData;
List 初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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); } } public ArrayList () { this .elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
add 添加元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 @Override public boolean add (E e) { ensureCapacityInternal(size + 1 ); elementData[size++] = e; return true ; } private void ensureCapacityInternal (int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity (Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity (int minCapacity) { if (minCapacity - elementData.length > 0 ) grow(minCapacity); } 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); } private static int hugeCapacity (int minCapacity) { if (minCapacity < 0 ) throw new OutOfMemoryError (); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
一维数组插入数据 GIF 动图
一维数组扩容
Java 数组扩容实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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); }
add 指定位置插入元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Override public void add (int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1 ); System.arraycopy(elementData, index, elementData, index + 1 , size - index); elementData[index] = element; size++; } private void rangeCheckForAdd (int index) { if (index > size || index < 0 ) throw new IndexOutOfBoundsException (outOfBoundsMsg(index)); }
set 替换元素 1 2 3 4 5 6 7 8 9 10 11 @Override public E set (int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
remove 删除元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public E remove (int index) { rangeCheck(index); E oldValue = elementData(index); int numMoved = size - index - 1 ; if (numMoved > 0 ) System.arraycopy(elementData, index + 1 , elementData, index, numMoved); elementData[--size] = null ; return oldValue; }