泛型数组列表_演练

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

posted @ 2024-11-04 11:13  xkfx  阅读(103)  评论(0编辑  收藏  举报