hi,今天我们来聊聊Java中的ArrayList~
ArrayList基础
ArrayList简介
ArrayList
是Java集合框架中的一个类,位于 java.util
包下。它实现了 List
接口以及其所有可选的接口,如 Serializable
(可序列化)和 Cloneable
(可克隆)。ArrayList
是一个动态数组,可以存储任意数量的非原始类型对象。
创建ArrayList实例
创建 ArrayList
实例非常简单,可以通过默认构造方法或带有初始容量的构造方法来实现。
默认构造方法
代码语言:javascript复制ArrayList<String> list = new ArrayList<>();
指定初始容量的构造方法
代码语言:javascript复制ArrayList<String> list = new ArrayList<>(10); // 初始容量为10
ArrayList的属性和初始容量
ArrayList
的初始容量是指在添加元素时不需要进行扩容操作的最小容量。如果初始容量设置得过高,而实际存储的元素较少,这将浪费内存;如果设置得过低,那么随着元素的增加,ArrayList
将不得不进行多次扩容操作,这将降低性能。
案例源码说明
创建并初始化ArrayList
代码语言:javascript复制// 创建一个空的ArrayList,用于存储String类型的对象
ArrayList<String> fruits = new ArrayList<>();
// 向ArrayList中添加元素
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");
// 打印ArrayList中的元素
System.out.println(fruits);
使用指定初始容量的构造方法
代码语言:javascript复制// 创建一个初始容量为5的ArrayList
ArrayList<Integer> numbers = new ArrayList<>(5);
// 向ArrayList中添加元素
numbers.add(1);
numbers.add(2);
numbers.add(3);
// 打印ArrayList的初始容量
System.out.println("Initial Capacity: " numbers.capacity());
ArrayList的扩容机制
ArrayList
的容量自动增长,但是当添加的元素超过当前容量时,会创建一个更大的数组来存储所有元素,并把旧的元素复制到新数组中。
// 创建一个初始容量为2的ArrayList
ArrayList<String> smallList = new ArrayList<>(2);
// 连续添加超过初始容量的元素,触发扩容
smallList.add("One");
smallList.add("Two");
smallList.add("Three");
// 打印当前容量
System.out.println("Current Capacity: " smallList.capacity());
ArrayList操作
元素添加
ArrayList
提供了多种添加元素的方法,包括在末尾添加单个元素或整个集合,以及在指定位置插入一个或多个元素。
add(E e)
在列表末尾添加一个元素。
示例:
代码语言:javascript复制ArrayList<String> list = new ArrayList<>();
// 添加单个元素到列表末尾
list.add("Element 1");
list.add("Element 2");
System.out.println(list); // 输出: [Element 1, Element 2]
add(int index, E element)
在指定位置插入一个元素。
示例:
代码语言:javascript复制ArrayList<String> list = new ArrayList<>();
list.add("First");
list.add("Third");
// 在索引 1 的位置插入 "Second"
list.add(1, "Second");
System.out.println(list); // 输出: [First, Second, Third]
元素访问
通过索引访问列表中的元素。
get(int index)
返回指定位置的元素。
示例:
代码语言:javascript复制ArrayList<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
// 获取索引1的元素
String fruit = list.get(1);
System.out.println(fruit); // 输出: Banana
元素删除
ArrayList
提供了几种删除元素的方法。
remove(int index)
删除指定位置的元素,并返回被删除的元素。
示例:
代码语言:javascript复制ArrayList<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
// 删除索引1的元素
String removed = list.remove(1);
System.out.println(removed); // 输出: Banana
System.out.println(list); // 输出: [Apple, Cherry]
remove(Object o)
删除列表中第一次出现的指定元素。
示例:
代码语言:javascript复制ArrayList<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
// 删除指定的对象
boolean isRemoved = list.remove("Banana");
System.out.println(isRemoved); // 输出: true
System.out.println(list); // 输出: [Apple]
元素搜索
ArrayList
提供了搜索元素的方法。
contains(Object o)
检查列表是否包含指定的元素。
示例:
代码语言:javascript复制ArrayList<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
// 检查列表是否包含 "Cherry"
boolean contains = list.contains("Cherry");
System.out.println(contains); // 输出: false
indexOf(Object o)
返回指定元素在列表中第一次出现的索引。
示例:
代码语言:javascript复制ArrayList<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
// 获取 "Banana" 在列表中的索引
int index = list.indexOf("Banana");
System.out.println(index); // 输出: 1
案例源码说明
以下是 ArrayList
操作的完整示例,包括添加、访问、删除和搜索元素:
public class ArrayListExample {
public static void main(String[] args) {
// 创建一个ArrayList
ArrayList<String> fruits = new ArrayList<>();
// 使用add方法添加元素到ArrayList
fruits.add("Apple");
fruits.add("Banana");
fruits.add(0, "Apricot"); // 在索引0的位置插入
// 使用get方法访问特定位置的元素
String fruit = fruits.get(1);
System.out.println("Fruit at index 1: " fruit);
// 使用remove方法删除特定位置的元素
String removedFruit = fruits.remove(2);
System.out.println("Removed fruit: " removedFruit);
// 使用contains方法检查元素是否存在
boolean hasCherry = fruits.contains("Cherry");
System.out.println("Contains Cherry: " hasCherry);
// 使用indexOf方法获取元素的索引
int indexOfBanana = fruits.indexOf("Banana");
System.out.println("Index of Banana: " indexOfBanana);
// 打印ArrayList的当前状态
System.out.println("Current fruits list: " fruits);
}
}
ArrayList高级特性
列表迭代
ArrayList
支持两种主要的迭代方式:使用传统的 for
循环和使用 Iterator
。
使用for-each
循环
代码语言:javascript复制ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C ");
// 使用for-each循环迭代ArrayList
for (String language : list) {
System.out.println(language);
}
使用迭代器
代码语言:javascript复制List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C ");
// 使用迭代器迭代ArrayList
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String language = iterator.next();
System.out.println(language);
}
列表排序
ArrayList
可以通过 Collections
类或自定义的 Comparator
进行排序。
Collections.sort()
代码语言:javascript复制List<String> languages = new ArrayList<>();
languages.add("Java");
languages.add("Python");
languages.add("C ");
// 使用Collections.sort()方法对ArrayList进行自然排序
Collections.sort(languages);
System.out.println(languages);
自定义排序
代码语言:javascript复制List<String> languages = new ArrayList<>();
languages.add("Java");
languages.add("Python");
languages.add("C ");
// 使用自定义Comparator进行排序
Collections.sort(languages, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s2.compareTo(s1); // 倒序排序
}
});
System.out.println(languages);
列表容量管理
ArrayList
允许开发者手动管理其容量。
ensureCapacity()
代码语言:javascript复制ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
// 确保ArrayList至少能容纳n个元素
numbers.ensureCapacity(10);
System.out.println("Current Capacity: " numbers.capacity());
trimToSize()
代码语言:javascript复制ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
// 调整ArrayList的容量以匹配其大小
numbers.trimToSize();
System.out.println("Trimmed Capacity: " numbers.capacity
());
案例源码说明
以下是 ArrayList
高级特性的完整示例,包括迭代、排序和容量管理:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
public class ArrayListAdvanced {
public static void main(String[] args) {
// 初始化ArrayList
ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C ");
// 迭代ArrayList
System.out.println("Iterating ArrayList:");
for (String language : list) {
System.out.println(language);
}
// 使用迭代器迭代ArrayList
System.out.println("Iterating with Iterator:");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String language = iterator.next();
System.out.println(language);
}
// 排序ArrayList
System.out.println("Sorted ArrayList (natural order):");
Collections.sort(list);
System.out.println(list);
// 使用自定义Comparator排序ArrayList
System.out.println("Sorted ArrayList (custom order):");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s2.compareTo(s1);
}
});
System.out.println(list);
// 容量管理
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
System.out.println("Before ensureCapacity: " numbers.capacity());
numbers.ensureCapacity(5);
System.out.println("After ensureCapacity: " numbers.capacity());
numbers.trimToSize();
System.out.println("After trimToSize: " numbers.capacity());
}
}
ArrayList与性能
时间复杂度分析
ArrayList
的性能通常取决于操作的类型。以下是一些常见操作的时间复杂度:
- 添加元素 (
add(E e)
,add(int index, E element)
): 平均时间复杂度为 O(1),但如果需要扩容,则为 O(n)。 - 获取元素 (
get(int index)
): 时间复杂度为 O(1)。 - 删除元素 (
remove(int index)
,remove(Object o)
): 时间复杂度为 O(n),因为可能需要移动元素。 - 搜索元素 (
contains(Object o)
,indexOf(Object o)
): 时间复杂度为 O(n)。
性能考量与优化建议
由于 ArrayList
是一个动态数组,它的性能特点需要根据使用场景来考虑:
- 频繁的插入和删除:如果对列表的中间位置进行频繁的插入和删除操作,性能会受到影响,因为
ArrayList
需要移动元素来维护数组的连续性。在这种情况下,考虑使用LinkedList
。 - 随机访问:如果需要频繁地进行随机访问,
ArrayList
是一个好的选择,因为它提供了 O(1) 的时间复杂度。 - 初始化容量:尽量预估列表的大小并设置合适的初始容量,以避免频繁的数组扩容操作。
案例源码说明
以下是 ArrayList
性能考量的示例:
频繁插入与删除的示例
代码语言:javascript复制ArrayList<String> list = new ArrayList<>();
// 模拟频繁的添加和删除操作
for (int i = 0; i < 10000; i ) {
list.add("Element " i);
if (i % 1000 == 0) {
list.remove(i - 1);
}
}
在这个示例中,我们模拟了一个包含频繁插入和删除的场景。如果列表很大,这会触发多次数组复制,导致性能下降。
随机访问的示例
代码语言:javascript复制ArrayList<String> list = new ArrayList<>();
// 填充ArrayList
for (int i = 0; i < 1000; i ) {
list.add("Element " i);
}
// 随机访问特定位置的元素
long startTime = System.nanoTime();
String element = list.get(999); // 假设我们要访问接近末尾的元素
long endTime = System.nanoTime();
System.out.println("Random access time: " (endTime - startTime) " ns");
在这个示例中,我们展示了 ArrayList
随机访问的性能。由于是直接通过索引访问,所以操作非常快速。
初始化容量的示例
代码语言:javascript复制ArrayList<String> list = new ArrayList<>(1000); // 设置初始容量为1000
// 填充ArrayList
for (int i = 0; i < 1000; i ) {
list.add("Element " i);
// 由于初始容量已足够,这里不会触发扩容操作
}