Java基础快速入门:从数组到集合,掌握ArrayList

本文纲要

  1. 集合与数组的区别 ------ 对象数组初体验
  2. ArrayList的构造方法与添加方法
  3. ArrayList常用成员方法(增删改查)
  4. 集合存储字符串并遍历
  5. 集合存储学生对象并遍历
  6. 键盘录入学生信息到集合
  7. 集合删除元素的注意事项
  8. 集合数据筛选 ------ 方法返回新集合

集合与数组的区别 ------ 对象数组初体验

数组和集合都是Java中用来存储数据的容器,但它们有一个关键区别:数组长度固定,集合长度可变。

在正式学习集合之前,我们先看一个案例:用数组存储自定义的学生对象。

需求

将(张三,23)、(李四,24)、(王五,25)这三组数据封装为三个学生对象,存入数组,然后遍历数组打印学生信息。

思路分析

  1. 定义学生类 Student,封装姓名和年龄
  2. 创建长度为3的 Student 类型数组
  3. 创建三个学生对象
  4. 将学生对象存入数组
  5. 遍历数组,取出每个学生对象,调用 getName()、getAge() 输出信息

代码实现

Student 类(位于 com.wb.domain 包):

java 复制代码
package com.wb.domain;
 
public class Student {
    private String name;
    private int age;
 
    public Student() {
    }
 
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
}

测试类 TestObjectArray

java 复制代码
package com.wb.array;
 
import com.wb.domain.Student;
 
public class TestObjectArray {
    public static void main(String[] args) {
        // 2. 动态初始化长度为3的数组,类型为Student类型 
        Student[] arr = new Student[3];
        // 3. 根据需求创建3个学生对象 
        Student stu1 = new Student("张三", 23);
        Student stu2 = new Student("李四", 24);
        Student stu3 = new Student("王五", 25);
        // 4. 将学生对象存入数组
        arr[0] = stu1;
        arr[1] = stu2;
        arr[2] = stu3;
        // 5. 遍历数组,取出每一个学生对象
        for (int i = 0; i < arr.length; i++) {
            Student temp = arr[i];
            System.out.println(temp.getName() + "..." + temp.getAge());
        }
    }
}

运行结果:

log 复制代码
张三...23
李四...24
王五...25 

数组的局限性

  • 如果此时来了一个新学生"赵六",数组已经满了,无法添加
  • 虽然可以手动创建新数组并复制旧数据,但非常麻烦
  • 我们期望有一个"懂事"的容器,能自动扩容,这就是集合

集合与数组的对比

  • 共同点:都是存储数据的容器
  • 不同点:
    • 数组长度固定,定义后不可改变
    • 集合长度可变,可以自动扩容
    • 应用场景:需要频繁增删数据时,优先使用集合

ArrayList的构造方法与添加方法

Java中集合类很多,基础入门我们先学习 ArrayList

它底层仍然是数组实现,但提供了自动扩容的能力

1 ) 创建ArrayList对象

构造方法:ArrayList(),创建一个初始容量为10的空列表。

使用泛型限制集合存储的数据类型,语法:ArrayList<数据类型> 变量名 = new ArrayList<>();

JDK 7 起,右侧尖括号可省略类型,自动推断。

java 复制代码
import java.util.ArrayList;
 
public class Demo1ArrayList {
    public static void main(String[] args) {
        // 创建只存储字符串的集合 
        ArrayList<String> list = new ArrayList<>();
        // 如果不加泛型,可以存储任意类型,但类型不安全,不推荐
    }
}

2 ) 添加元素

ArrayList 提供了两个添加方法:

方法 说明
boolean add(E e) 将元素添加到列表末尾
void add(int index, E element) 将元素插入到指定索引位置(插队)

示例:

java 复制代码
ArrayList<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
list.add("444");
list.add("555");
 
list.add(0, "666");  // 在索引0处插入 
 
System.out.println(list);  // 输出:[666, 111, 222, 333, 444, 555]

注意:

如果插入时索引越界,会抛出 IndexOutOfBoundsException

集合对象直接打印不会出现内存地址,而是显示元素内容(底层重写了 toString)。

ArrayList常用成员方法(增删改查)

ArrayList 提供了丰富的增删改查方法,常用如下:

方法 说明
boolean add(E e) 尾部添加元素
void add(int index, E e) 指定位置插入元素
boolean remove(Object o) 删除指定元素,返回是否成功
E remove(int index) 删除指定索引元素,返回被删除的元素
E set(int index, E e) 修改指定索引元素,返回被修改前的元素
E get(int index) 获取指定索引元素
int size() 返回集合中元素个数

演示代码(整合删除、修改、查询):

java 复制代码
import java.util.ArrayList;
 
public class Demo2ArrayList {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
        list.add("111");
        list.add("222");
        list.add("333");
        list.add("444");
        list.add("555");
 
        // 删除:根据元素删除
        boolean b1 = list.remove("abc");   // 删除成功返回true
        boolean b2 = list.remove("zzz");   // 不存在返回false
        System.out.println(b1);            // true
        System.out.println(b2);            // false
 
        // 删除:根据索引删除 
        String removed = list.remove(0);   // 删除索引0的元素,返回被删除的元素
        System.out.println(removed);       // 111 
 
        // 修改
        String old = list.set(0, "666");   // 将索引0元素改为"666",返回旧值
        System.out.println(old);           // 222 
 
        // 查询
        String s1 = list.get(0);           // 获取索引0元素
        String s2 = list.get(1);
        System.out.println(s1);            // 666 
        System.out.println(s2);            // 333
 
        // 获取集合大小
        int size = list.size();
        System.out.println(size);          // 4
 
        System.out.println(list);          // [666, 333, 444, 555]
    }
}

提示:除了 get 和 size 外,删除、修改方法的返回值通常不需要接收,直接调用即可。

集合存储字符串并遍历

遍历集合的关键是结合 size()get(int index) 方法,格式与遍历数组非常相似。

需求:创建存储字符串的集合,存入3个字符串,遍历打印。

代码实现:

java 复制代码
import java.util.ArrayList;
 
public class Test1 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
 
        // 通用遍历格式 
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            System.out.println(s);
        }
    }
}

遍历快捷键:在IDEA中输入 list.fori 回车,可快速生成循环骨架。

集合存储学生对象并遍历

当集合存储自定义对象时,遍历取出的是对象引用,需要调用对象方法才能获取具体信息。

需求:创建存储 Student 对象的集合,存入3个学生,遍历打印学生姓名和年龄。

代码实现:

java 复制代码
import com.wb.domain.Student;
import java.util.ArrayList;
 
public class Test2 {
    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<>();
 
        Student stu1 = new Student("张三1", 23);
        Student stu2 = new Student("张三2", 23);
        Student stu3 = new Student("张三3", 23);
 
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);
 
        for (int i = 0; i < list.size(); i++) {
            Student stu = list.get(i);
            System.out.println(stu.getName() + "..." + stu.getAge());
        }
    }
}

注意:如果直接打印 stu,输出的是对象地址,必须通过 getName() 等方法获取属性值。

键盘录入学生信息到集合

将之前手动创建的学生数据改为从键盘录入,使程序更灵活。

需求:键盘录入3个学生的姓名和年龄,封装成对象存入集合,遍历集合输出信息。

关键点:

使用 Scanner 接收键盘输入

为避免 nextLine()nextInt() 混用导致的问题,这里使用 next() 接收字符串

将录入及封装逻辑抽取为方法 getStudent(),提高复用性

代码实现:

java 复制代码
import com.wb.domain.Student;
import java.util.ArrayList;
import java.util.Scanner;
 
public class Test3 {
    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<>();
 
        Student stu1 = getStudent();
        Student stu2 = getStudent();
        Student stu3 = getStudent();
 
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);
 
        for (int i = 0; i < list.size(); i++) {
            Student stu = list.get(i);
            System.out.println(stu.getName() + "..." + stu.getAge());
        }
    }
 
    public static Student getStudent() {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入学生姓名:");
        String name = sc.next();
        System.out.println("请输入学生年龄:");
        int age = sc.nextInt();
        return new Student(name, age);
    }
}

运行示例:

log 复制代码
请输入学生姓名:
张三
请输入学生年龄:
23
请输入学生姓名:
李四
请输入学生年龄:
24
请输入学生姓名:
王五
请输入学生年龄:
25
张三...23
李四...24
王五...25

集合删除元素的注意事项

删除集合中的元素时,如果使用 remove(Object o) 方法,它只会删除第一个匹配的元素。

若要删除所有匹配的元素,必须遍历集合,并采用索引删除的方式,同时注意索引回退问题。

需求:删除集合中所有 "test" 字符串,集合初始元素为 "test", "张三", "李四", "test", "test"

错误做法:直接 list.remove("test") 只删除了第一个 "test"

正确做法:遍历时用索引删除,并在删除后执行 i--,防止漏掉连续的元素

删除过程图解

假设集合变化如下:
#mermaid-svg-xDcsvkn1sR10qWC9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xDcsvkn1sR10qWC9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xDcsvkn1sR10qWC9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xDcsvkn1sR10qWC9 .error-icon{fill:#552222;}#mermaid-svg-xDcsvkn1sR10qWC9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xDcsvkn1sR10qWC9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xDcsvkn1sR10qWC9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xDcsvkn1sR10qWC9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xDcsvkn1sR10qWC9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xDcsvkn1sR10qWC9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xDcsvkn1sR10qWC9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xDcsvkn1sR10qWC9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xDcsvkn1sR10qWC9 .marker.cross{stroke:#333333;}#mermaid-svg-xDcsvkn1sR10qWC9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xDcsvkn1sR10qWC9 p{margin:0;}#mermaid-svg-xDcsvkn1sR10qWC9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xDcsvkn1sR10qWC9 .cluster-label text{fill:#333;}#mermaid-svg-xDcsvkn1sR10qWC9 .cluster-label span{color:#333;}#mermaid-svg-xDcsvkn1sR10qWC9 .cluster-label span p{background-color:transparent;}#mermaid-svg-xDcsvkn1sR10qWC9 .label text,#mermaid-svg-xDcsvkn1sR10qWC9 span{fill:#333;color:#333;}#mermaid-svg-xDcsvkn1sR10qWC9 .node rect,#mermaid-svg-xDcsvkn1sR10qWC9 .node circle,#mermaid-svg-xDcsvkn1sR10qWC9 .node ellipse,#mermaid-svg-xDcsvkn1sR10qWC9 .node polygon,#mermaid-svg-xDcsvkn1sR10qWC9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xDcsvkn1sR10qWC9 .rough-node .label text,#mermaid-svg-xDcsvkn1sR10qWC9 .node .label text,#mermaid-svg-xDcsvkn1sR10qWC9 .image-shape .label,#mermaid-svg-xDcsvkn1sR10qWC9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-xDcsvkn1sR10qWC9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xDcsvkn1sR10qWC9 .rough-node .label,#mermaid-svg-xDcsvkn1sR10qWC9 .node .label,#mermaid-svg-xDcsvkn1sR10qWC9 .image-shape .label,#mermaid-svg-xDcsvkn1sR10qWC9 .icon-shape .label{text-align:center;}#mermaid-svg-xDcsvkn1sR10qWC9 .node.clickable{cursor:pointer;}#mermaid-svg-xDcsvkn1sR10qWC9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xDcsvkn1sR10qWC9 .arrowheadPath{fill:#333333;}#mermaid-svg-xDcsvkn1sR10qWC9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xDcsvkn1sR10qWC9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xDcsvkn1sR10qWC9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xDcsvkn1sR10qWC9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xDcsvkn1sR10qWC9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xDcsvkn1sR10qWC9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xDcsvkn1sR10qWC9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xDcsvkn1sR10qWC9 .cluster text{fill:#333;}#mermaid-svg-xDcsvkn1sR10qWC9 .cluster span{color:#333;}#mermaid-svg-xDcsvkn1sR10qWC9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xDcsvkn1sR10qWC9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xDcsvkn1sR10qWC9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-xDcsvkn1sR10qWC9 .icon-shape,#mermaid-svg-xDcsvkn1sR10qWC9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xDcsvkn1sR10qWC9 .icon-shape p,#mermaid-svg-xDcsvkn1sR10qWC9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xDcsvkn1sR10qWC9 .icon-shape .label rect,#mermaid-svg-xDcsvkn1sR10qWC9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xDcsvkn1sR10qWC9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xDcsvkn1sR10qWC9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xDcsvkn1sR10qWC9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



开始遍历 i=0
list.get i 是否为test?
list.remove i
i--
i++
i < list.size?
结束

为什么需要 i--?

删除当前元素后,后面所有元素会向前移动一位,如果直接 i++,就会跳过原本下一个位置的元素。

先 i-- 再 i++(循环自带 i++),相当于指针原地不动,重新检查当前位置的新元素,确保不遗漏。

代码实现:

java 复制代码
import java.util.ArrayList;
 
public class Test4 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("test");
        list.add("张三");
        list.add("李四");
        list.add("test");
        list.add("test");
 
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            // 常量.equals(变量) 可避免空指针异常
            if ("test".equals(s)) {
                list.remove(i);
                i--;  // 关键:回退索引
            }
        }
        System.out.println(list);  // 输出:[张三, 李四]
    }
}

最佳实践:字符串比较时,建议用常量调用 equals,如 "test".equals(s),避免 null 引发空指针异常。

集合数据筛选 ------ 方法返回新集合

本节演示如何编写一个方法,接收集合并根据条件筛选出部分元素返回新集合

需求:定义一个方法,接收一个 ArrayList<Student> 集合,将年龄低于18的学生对象筛选出来,存入新集合并返回

实现步骤

  1. 定义方法 getList,参数为 ArrayList<Student>,返回值也为 ArrayList<Student>
  2. 方法内部创建新集合 newList
  3. 遍历原集合,获取每个学生对象的年龄
  4. 判断年龄是否小于18,若满足则添加到 newList
  5. 返回新集合

代码实现:

java 复制代码
import com.wb.domain.Student;
import java.util.ArrayList;
 
public class Test5 {
    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<>();
        list.add(new Student("张三1", 10));
        list.add(new Student("张三2", 10));
        list.add(new Student("张三3", 20));
 
        ArrayList<Student> newList = getList(list);
 
        for (int i = 0; i < newList.size(); i++) {
            Student stu = newList.get(i);
            System.out.println(stu.getName() + "..." + stu.getAge());
        }
    }
 
    public static ArrayList<Student> getList(ArrayList<Student> list) {
        ArrayList<Student> newList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            Student stu = list.get(i);
            if (stu.getAge() < 18) {
                newList.add(stu);
            }
        }
        return newList;
    }
}

运行结果:

log 复制代码
张三1...10 
张三2...10 

要点:

方法的形参和返回值可以是集合类型,调用时传递对应集合对象即可

这种"筛选并返回新集合"的模式在实际开发中非常常见,它为后续学习流式操作打下了基础

总结

通过本文,我们从对象数组入手,体会了数组的局限性,进而引入了 ArrayList 集合

学习了集合的构造、增删改查、遍历、键盘录入、删除注意事项以及筛选操作

掌握了这些基础,你就已经能够利用集合解决许多动态数据存储的问题了

在后续的学习中,还会接触到更多高级的集合类,但万变不离其宗,理解好 ArrayList 是第一步