java集合浅谈——List
一、类库结构图概览
Java中集合类库的结构图,如下所示:
图1
图2
二、Collection接口说明
(1)Collection是最基本的集合接口,由Collection接口派生的两个接口是List和Set。
(2)所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数和有一个Collection类型参数的构造函数。前者用于创建一个空的Collection,后者用于创建一个新的Collection,允许用户复制一个Collection。
(3)不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,可逐一访问Collection中每一个元素。用法如下:
Iterator it = collection.iterator(); // 获得一个迭代子 while(it.hasNext()) { Object obj = it.next(); // 得到下一个元素 }
Note:
容器对象仅能持有对象引用(对象的指针),而不是Copy对象信息
三、List源码解读
List以某种插入顺序来维护元素顺序,另外元素可以重复。
ArrayList
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList是用数组(Object类型的数组,被标注为transient)来实现的,读取速度快,插入与删除速度慢(因为插入与删除时要移动后面的元素),适合于随机访问。
ArrayList是动态增长的,并确保始终存在添加元素的空间。
初始化ArrayList时可以指定容量,源代码如下:
/** * 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); } }
通过add(E e)方法添加元素时,可能需要调整内部数组的大小,代码如下:
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { modCount++; add(e, elementData, size); return true; }
/** * This helper method split out from add(E) to keep method * bytecode size under 35 (the -XX:MaxInlineSize default value), * which helps when add(E) is called in a C1-compiled loop. */ private void add(E e, Object[] elementData, int s) { if (s == elementData.length) elementData = grow(); elementData[s] = e; size = s + 1; }
问题:How does the remove method work in ArrayList? [ArrayList shrinks automatically ]
可以使用remove(int i) 或remove(Object o)从ArrayList中删除一个元素。
当移除任何元素时,内部所有后续元素都要左移,以填补被移除元素的空白,数组的大小将减 1。
/** * 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} */ public E remove(int index) { Objects.checkIndex(index, size); final Object[] es = elementData; @SuppressWarnings("unchecked") E oldValue = (E) es[index]; fastRemove(es, index); return oldValue; }
/** * Private remove method that skips bounds checking and does not * return the value removed. */ private void fastRemove(Object[] es, int i) { modCount++; final int newSize; if ((newSize = size - 1) > i) System.arraycopy(es, i + 1, es, i, newSize - i); es[size = newSize] = null; }
扩展阅读:
LinkedList
是用双向链表来实现的,删除与插入速度快,读取速度较慢,因为它读取时是从头向尾或从尾向头查找元素,适合于元素的插入与删除操作。
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
问题:How add() method works in a LinkedList
/** * Appends the specified element to the end of this list. * * <p>This method is equivalent to {@link #addLast}. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { linkLast(e); return true; }
/** * Links e as last element. */ void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
内部数据结构是双向链表,示意图如下:
对应源代码如下:
private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
问题:How remove() method works in Java LinkedList class
当调用remove()方法时,必须更改删除节点左侧和右侧节点的引用,源代码如下:
/** * Removes the element at the specified position in this list. Shifts any * subsequent elements to the left (subtracts one from their indices). * Returns the element that was removed from the list. * * @param index the index of the element to be removed * @return the element previously at the specified position * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { checkElementIndex(index); return unlink(node(index)); }
/** * Unlinks non-null node x. */ E unlink(Node<E> x) { // assert x != null; final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
Vector
功能与ArrayList几乎相同,也是用数组来实现的,其添加、删除等都是基于线程同步的,源代码如下:
/** * Appends the specified element to the end of this Vector. * * @param e element to be appended to this Vector * @return {@code true} (as specified by {@link Collection#add}) * @since 1.2 */ public synchronized boolean add(E e) { modCount++; add(e, elementData, elementCount); return true; }
初始化Vector时可以设定容量,源代码如下:
/** * Constructs an empty vector with the specified initial capacity and * with its capacity increment equal to zero. * * @param initialCapacity the initial capacity of the vector * @throws IllegalArgumentException if the specified initial capacity * is negative */ public Vector(int initialCapacity) { this(initialCapacity, 0); }
如果以new Vector()方式创建Vector,则其初始容量为10;
如果以new Vector(int initialCapacity, int capacityIncrement)方式创建Vector,则其初始容量为initialCapacity。
Stack
Stack继承自Vector,实现一个后进先出的堆栈,线程同步。
除了基本的入栈和出栈操作,该类还提供了另外几个函数,如:peek、search,对应源代码如下:
/** * Looks at the object at the top of this stack without removing it * from the stack. * * @return the object at the top of this stack (the last item * of the {@code Vector} object). * @throws EmptyStackException if this stack is empty. */ public synchronized E peek() { int len = size(); if (len == 0) throw new EmptyStackException(); return elementAt(len - 1); }
Note:
Stack是继承自Java中的Vector的遗留类,当我们不需要线程安全时会有额外的开销。
建议使用ArrayDeque来实现堆栈,因为它在单线程环境中效率更高。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?