泛型数组列表_演练
ex1:基础练习
基础练习请尽量独立完成(不要借助AI,实在不会才看参考代码)。
原始数组
学生类:
package ex1;
public class Student {
protected String name;
protected int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "我叫" + name + ", 我考了" + score + ", 我很开心";
}
}
测试原始数组:
package ex1.test;
import ex1.Student;
public class ArrayTest {
public static void main(String[] args) {
Student[] students = new Student[3 + 100];
students[0] = new Student("张三", 11);
students[1] = new Student("李四", 91);
students[2] = new Student("王五", 11);
System.out.println(students[1]);
System.out.println("************************");
for (int i = 0; i != 100; ++i) {
students[3 + i] = new Student("李某某", i + 1);
}
System.out.println(students[50 - 1]);
System.out.println("************************");
for (int i = 50 - 1; i != 3 + 100 - 1; ++i) {
students[i] = students[i + 1];
}
students[3 + 100 - 1] = null;
System.out.println(students[50 - 1]);
}
}
参考输出:
我叫李四, 我考了91, 我很开心
************************
我叫李某某, 我考了47, 我很开心
************************
我叫李某某, 我考了48, 我很开心
ArrayList
测试数组列表:
package ex1.test;
import ex1.Student;
import java.util.ArrayList;
public class ArrayListTest {
public static void main(String[] args) {
/*
ArrayList是一个有类型参数的 泛型类,为了指定数组列表保存的元素类型
需要用一对尖括号将类名括起来追加到ArrayList的后面,例如ArrayList<Student>
*/
// 0 下面声明并构造一个保存Student对象的数组
//原始写法 ArrayList<Student> students = new ArrayList<Student>();
//jdk7可以省略右边的类型参数
// ArrayList<Student> students = new ArrayList<>();
//jdk10可以用var关键字避免重复写类名
var students = new ArrayList<Student>();
// 1 使用 add 方法可以将元素添加到数组列表中
students.add(new Student("张三", 11));
students.add(new Student("李四", 91));
students.add(new Student("王五", 11));
// 2 要得到一个数组列表的元素,用x.get(i)等价于x[i]
System.out.println(students.get(1));
System.out.println("************************");
for (int i = 0; i != 100; ++i) {
students.add(new Student("李某某", i + 1));
}
System.out.println(students.get(50 - 1));
// 3 从数组列表中间删除一个元素—— remove
System.out.println("************************");
students.remove(50 - 1);
System.out.println(students.get(50 - 1));
// 4 置换——set, x.set(i, liDaTou)等价于x[i] = liDaTou
System.out.println("************************");
students.set(50 - 1, new Student("xxx",100));
System.out.println(students.get(50 - 1));
}
}
参考输出:
我叫李四, 我考了91, 我很开心
************************
我叫李某某, 我考了47, 我很开心
************************
我叫李某某, 我考了48, 我很开心
************************
我叫xxx, 我考了100, 我很开心
自定义ArrayList(MyArrayList)
TODO表示要填空的地方,建议不要一次全部复制粘贴,逐个方法进行调试。
理解并填空:
package ex1;
public class MyArrayList {
// 初始容量
private static final int INITIAL_CAPACITY = 10;
// 存储元素的数组
private Object[] elements;
// 当前元素的数量
private int size = 0;
// 初始化数组容量
public MyArrayList() {
elements = new Object[INITIAL_CAPACITY];
}
// 添加元素
public void add(Object element) {
// 检查容量是否足够,不够则扩容
// TODO
}
// 获取指定位置的元素
public Object get(int index) {
if (index >= 0 && index < size) {
// TODO
} else {
System.out.println("错误:索引 " + index + " 不合法!");
return null; // 返回 null 表示获取失败
}
}
// 删除指定位置的元素
public Object remove(int index) {
if (index >= 0 && index < size) {
Object removedElement = elements[index];
// 将后面的元素向前移动
for (int i = index; i < size - 1; i++) {
// TODO
}
// 更新大小并清除最后一个元素的引用
elements[size - 1] = null;
size--;
return removedElement;
} else {
System.out.println("错误:索引 " + index + " 不合法!");
return null; // 返回 null 表示删除失败
}
}
// 设置指定位置的元素,并返回被替换的旧元素
public Object set(int index, Object element) {
if (index >= 0 && index < size) {
// TODO
} else {
System.out.println("错误:索引 " + index + " 不合法!");
return null; // 返回 null 表示设置失败
}
}
// 获取当前元素数量
public int size() {
return size;
}
// 扩容方法
private void grow() {
int newCapacity = elements.length * 2;
Object[] newElements = new Object[newCapacity];
// 复制旧数组内容到新数组
for (int i = 0; i < elements.length; i++) {
newElements[i] = elements[i];
}
elements = newElements;
}
}
测试我的数组列表:
package ex1.test;
import ex1.MyArrayList;
import ex1.Student;
public class MyArrayListTest {
public static void main(String[] args) {
var students = new MyArrayList();
// 1 增 查
students.add(new Student("张三", 11));
students.add(new Student("李四", 91));
students.add(new Student("王五", 11));
System.out.println(students.get(1));
System.out.println("************************");
for (int i = 0; i != 100; ++i) {
students.add(new Student("李某某", i + 1));
}
System.out.println(students.get(50 - 1));
System.out.println("************************");
// 2 删
students.remove(50 - 1);
System.out.println(students.get(50 - 1));
System.out.println("************************");
// 3 替换
students.set(50 - 1, new Student("xxx",100));
System.out.println(students.get(50 - 1));
}
}
参考输出:
我叫李四, 我考了91, 我很开心
************************
我叫李某某, 我考了47, 我很开心
************************
我叫李某某, 我考了48, 我很开心
************************
我叫xxx, 我考了100, 我很开心
ex2:拓展探究
请注意,以下测试方法未必靠谱,且结论仅供参考未必有实际意义(只有在特定场景下才会复制超大数组)。
三种数组复制方式(for循环、System.arraycopy 和 Arrays.copyOf)的性能测试
package ex2;
import java.util.Arrays;
public class ArrayCopyPerformanceTest {
public static void main(String[] args) {
// 定义不同的数据量大小
int[] sizes = {1000, 10000, 100000, 1000000}; // 1千、1万、10万、100万
System.out.println("数组复制性能测试 (单位:毫秒):\n");
for (int size : sizes) {
System.out.println("数组大小: " + size);
// 创建一个随机的整数数组
int[] originalArray = new int[size];
for (int i = 0; i < size; i++) {
originalArray[i] = i;
}
// 1. 使用 for 循环复制数组
long startTime = System.nanoTime();
int[] forCopiedArray = new int[size];
for (int i = 0; i < size; i++) {
forCopiedArray[i] = originalArray[i];
}
long endTime = System.nanoTime();
long forLoopTime = (endTime - startTime) / 1_000_000; // 转换为毫秒
System.out.println("for循环复制时间: " + forLoopTime + " 毫秒");
// 2. 使用 System.arraycopy 复制数组
startTime = System.nanoTime();
int[] systemArrayCopiedArray = new int[size];
System.arraycopy(originalArray, 0, systemArrayCopiedArray, 0, size);
endTime = System.nanoTime();
long systemArrayCopyTime = (endTime - startTime) / 1_000_000;
System.out.println("System.arraycopy复制时间: " + systemArrayCopyTime + " 毫秒");
// 3. 使用 Arrays.copyOf 复制数组
startTime = System.nanoTime();
int[] arraysCopyOfArray = Arrays.copyOf(originalArray, size);
endTime = System.nanoTime();
long arraysCopyOfTime = (endTime - startTime) / 1_000_000;
System.out.println("Arrays.copyOf复制时间: " + arraysCopyOfTime + " 毫秒");
// 打印一个空行作为分隔
System.out.println();
}
}
}
参考输出:
数组复制性能测试 (单位:毫秒):
数组大小: 1000
for循环复制时间: 0 毫秒
System.arraycopy复制时间: 0 毫秒
Arrays.copyOf复制时间: 0 毫秒
数组大小: 10000
for循环复制时间: 0 毫秒
System.arraycopy复制时间: 0 毫秒
Arrays.copyOf复制时间: 0 毫秒
数组大小: 100000
for循环复制时间: 0 毫秒
System.arraycopy复制时间: 0 毫秒
Arrays.copyOf复制时间: 0 毫秒
数组大小: 1000000
for循环复制时间: 2 毫秒
System.arraycopy复制时间: 1 毫秒
Arrays.copyOf复制时间: 1 毫秒
传统数组在性能上有优势吗?
package ex2;
import java.util.ArrayList;
public class ArrayVsArrayListPerformanceTest {
public static void main(String[] args) {
int size = 10_000_000; // 测试的元素数量
System.out.println(size);
// 创建传统数组和 ArrayList
int[] array = new int[size];
ArrayList<Integer> arrayList = new ArrayList<>(size);
// 初始化数据
for (int i = 0; i < size; i++) {
array[i] = i;
arrayList.add(i);
}
// 1. 测试访问速度
// 测试传统数组访问速度
long startTime = System.nanoTime();
long arraySum = 0;
for (int i = 0; i < size; i++) {
arraySum += array[i];
}
long endTime = System.nanoTime();
long arrayAccessTime = (endTime - startTime) / 1_000_000; // 转换为毫秒
System.out.println("数组访问时间: " + arrayAccessTime + " 毫秒");
// 测试 ArrayList 访问速度
startTime = System.nanoTime();
long arrayListSum = 0;
for (int i = 0; i < size; i++) {
arrayListSum += arrayList.get(i);
}
endTime = System.nanoTime();
long arrayListAccessTime = (endTime - startTime) / 1_000_000;
System.out.println("ArrayList访问时间: " + arrayListAccessTime + " 毫秒");
// 2. 内存占用对比(通过简单计算估算)
// 传统数组内存:4 字节 * 元素数量(int 每个占 4 字节)
long arrayMemoryUsage = 4L * size;
System.out.println("估算数组内存占用: " + arrayMemoryUsage / (1024 * 1024) + " MB");
// ArrayList 内存:每个 Integer 对象约占 16 字节,加上 ArrayList 自身结构的开销
long arrayListMemoryUsage = 16L * size + 64; // 假设 ArrayList 自身占用 64 字节
System.out.println("估算 ArrayList 内存占用: " + arrayListMemoryUsage / (1024 * 1024) + " MB");
}
}
参考输出:
测试的元素数量:10000000
数组访问时间: 4 毫秒
ArrayList访问时间: 12 毫秒
估算数组内存占用: 38 MB
估算 ArrayList 内存占用: 152 MB