目录
- [一、List 集合概述与特性](#一、List 集合概述与特性)
-
- [1.1 集合框架体系](#1.1 集合框架体系)
- [1.2 List 集合特性](#1.2 List 集合特性)
- [1.3 List 接口常用方法](#1.3 List 接口常用方法)
- [二、ArrayList 集合实战](#二、ArrayList 集合实战)
-
- [2.1 ArrayList 底层结构](#2.1 ArrayList 底层结构)
- [2.2 ArrayList 常用操作](#2.2 ArrayList 常用操作)
- [2.3 ArrayList 扩容机制](#2.3 ArrayList 扩容机制)
- [2.4 ArrayList 线程安全性问题与解决](#2.4 ArrayList 线程安全性问题与解决)
- [三、LinkedList 集合实战](#三、LinkedList 集合实战)
-
- [3.1 LinkedList 底层结构](#3.1 LinkedList 底层结构)
- [3.2 LinkedList 常用操作](#3.2 LinkedList 常用操作)
- [3.3 LinkedList 与 ArrayList 的性能对比](#3.3 LinkedList 与 ArrayList 的性能对比)
- [3.4 List 集合实战案例(学生信息管理系统初步)](#3.4 List 集合实战案例(学生信息管理系统初步))
一、List 集合概述与特性
1.1 集合框架体系
Java 集合框架为我们提供了一套强大且灵活的工具,用于存储和操作对象集合。它主要由接口、实现类和算法组成,是 Java 编程中不可或缺的一部分。其中,Collection接口是集合框架的根接口,它定义了集合的基本操作,比如添加元素、删除元素以及判断集合是否为空等。
List接口和Set接口都继承自Collection接口,它们代表了不同类型的集合。List接口代表一个有序的集合,允许元素重复,就像是一个有序的列表,你可以按照元素添加的顺序来访问它们,并且可以有多个相同的元素。而Set接口代表一个无序的、不允许元素重复的集合,类似于数学中的集合概念,每个元素都是唯一的。
Map接口则用于存储键值对,一个键最多映射到一个值,它和Collection接口是并列的关系,并不继承自Collection。Map接口的常用实现类有HashMap和TreeMap等,HashMap基于哈希表实现,插入和查找效率高,但不保证键值对的顺序;TreeMap基于红黑树实现,键按自然顺序或自定义顺序排序。
List接口在集合框架中起着重要的作用,它为我们提供了一种有序、可重复的集合实现方式。通过List接口,我们可以方便地对元素进行添加、删除、查找等操作,并且能够根据元素的索引来快速访问特定位置的元素。在实际开发中,List接口的应用非常广泛,比如存储用户列表、商品列表等需要保持顺序和允许重复元素的场景。
1.2 List 集合特性
- 有序性:List集合中的元素是按照添加的顺序进行存储的,这意味着我们可以按照元素插入的先后顺序来访问它们。例如:
java
import java.util.ArrayList;
import java.util.List;
public class ListOrderExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
for (String fruit : list) {
System.out.println(fruit);
}
}
}
在上述代码中,我们依次向List集合中添加了 "apple"、"banana" 和 "cherry",然后通过增强for循环遍历集合,输出的结果将按照添加的顺序依次为 "apple"、"banana"、"cherry"。
- 可重复性:List集合允许存储重复的元素,这使得我们可以在集合中保存多个相同的对象。例如:
java
import java.util.ArrayList;
import java.util.List;
public class ListDuplicateExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("apple");
for (String fruit : list) {
System.out.println(fruit);
}
}
}
在这个例子中,我们向List集合中添加了两个 "apple",集合并不会因为元素重复而拒绝添加,输出结果将包含两个 "apple"。
- 元素可索引:List集合中的每个元素都有一个对应的索引值,我们可以通过索引来快速访问和操作特定位置的元素。索引从 0 开始,依次递增。例如:
java
import java.util.ArrayList;
import java.util.List;
public class ListIndexExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
String secondFruit = list.get(1);
System.out.println("第二个水果是:" + secondFruit);
list.set(1, "orange");
System.out.println("修改后的集合:" + list);
}
}
在这段代码中,我们使用get方法通过索引 1 获取到集合中的第二个元素 "banana",然后使用set方法将索引 1 位置的元素修改为 "orange",通过索引操作,我们可以方便地对集合中的元素进行读取和修改。
1.3 List 接口常用方法
- add 方法:用于向List集合中添加元素,有两种重载形式。一种是add(E e),将指定的元素添加到集合的末尾;另一种是add(int index, E element),将指定元素插入到集合的指定位置。例如:
java
import java.util.ArrayList;
import java.util.List;
public class ListAddExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add(1, "cherry");
System.out.println(list);
}
}
在上述代码中,我们首先使用add(E e)方法向集合中添加了 "apple" 和 "banana",然后使用add(int index, E element)方法将 "cherry" 插入到索引 1 的位置,最终集合中的元素顺序为 "apple"、"cherry"、"banana"。
- remove 方法:用于从List集合中删除元素,也有两种重载形式。remove(int index)方法用于移除指定索引位置的元素,并返回被移除的元素;remove(Object o)方法用于移除集合中第一个与指定对象相等的元素,如果集合中存在该元素,则返回true,否则返回false。例如:
java
import java.util.ArrayList;
import java.util.List;
public class ListRemoveExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
String removedFruit = list.remove(1);
System.out.println("被移除的水果是:" + removedFruit);
System.out.println("移除后的集合:" + list);
boolean removed = list.remove("cherry");
System.out.println("是否移除cherry:" + removed);
System.out.println("再次移除后的集合:" + list);
}
}
在这个例子中,我们先使用remove(int index)方法移除了索引 1 位置的 "banana",然后使用remove(Object o)方法移除了 "cherry",并通过返回值判断是否移除成功。
- get 方法:get(int index)方法用于返回List集合中指定索引位置的元素。例如:
java
import java.util.ArrayList;
import java.util.List;
public class ListGetExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
String thirdFruit = list.get(2);
System.out.println("第三个水果是:" + thirdFruit);
}
}
通过get(2)方法,我们获取到了集合中索引为 2 的元素 "cherry"。
- set 方法:set(int index, E element)方法用于用指定元素替换List集合中指定索引位置的元素,并返回被替换的元素。例如:
java
import java.util.ArrayList;
import java.util.List;
public class ListSetExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
String replacedFruit = list.set(1, "orange");
System.out.println("被替换的水果是:" + replacedFruit);
System.out.println("替换后的集合:" + list);
}
}
在上述代码中,我们使用set方法将索引 1 位置的 "banana" 替换为 "orange",并返回了被替换的 "banana"。这些常用方法是我们操作List集合的基础,熟练掌握它们能够帮助我们高效地处理集合中的数据。
二、ArrayList 集合实战
2.1 ArrayList 底层结构
ArrayList是List接口的一个重要实现类,它的底层结构基于动态数组。在ArrayList中,维护了一个Object类型的数组elementData,用于存储元素,如下所示:
java
private transient Object[] elementData;
这里的transient关键字表示该数组在进行对象序列化时不会被保存,因为ArrayList的容量是动态变化的,序列化整个数组可能会造成不必要的空间浪费和性能开销。
当我们创建一个ArrayList对象时,如果使用无参构造函数,elementData会被初始化为一个空数组:
java
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
而当使用指定初始容量的构造函数时,elementData会被初始化为指定大小的数组:
java
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);
}
}
private static final Object[] EMPTY_ELEMENTDATA = {};
这种基于动态数组的底层结构,使得ArrayList在存储元素时,能够像数组一样通过索引快速访问元素,时间复杂度为O(1)。但由于数组长度固定,当需要添加更多元素而数组已满时,就需要进行扩容操作,这会涉及到创建新数组、复制元素等操作,对性能有一定影响。
2.2 ArrayList 常用操作
- 添加元素 :
ArrayList提供了add(E e)和add(int index, E element)方法用于添加元素。add(E e)方法将元素添加到列表末尾,时间复杂度为O(1)(不考虑扩容情况),示例代码如下:
java
import java.util.ArrayList;
import java.util.List;
public class ArrayListAddExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
System.out.println(list);
}
}
add(int index, E element)方法将元素插入到指定索引位置,由于插入元素后,后续元素需要向后移动,所以时间复杂度为O(n),示例代码如下:
java
import java.util.ArrayList;
import java.util.List;
public class ArrayListInsertExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add(1, "cherry");
System.out.println(list);
}
}
- 删除元素 :
remove(int index)方法用于移除指定索引位置的元素,移除元素后,后续元素需要向前移动,时间复杂度为O(n),示例代码如下:
java
import java.util.ArrayList;
import java.util.List;
public class ArrayListRemoveExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
String removedFruit = list.remove(1);
System.out.println("被移除的水果是:" + removedFruit);
System.out.println("移除后的集合:" + list);
}
}
remove(Object o)方法用于移除集合中第一个与指定对象相等的元素,如果存在该元素,则返回true,否则返回false,同样时间复杂度为O(n),因为需要遍历集合查找要删除的元素。
- 遍历元素 :
可以使用普通for循环、增强for循环和迭代器来遍历ArrayList。普通for循环通过索引访问元素,效率较高,示例代码如下:
java
import java.util.ArrayList;
import java.util.List;
public class ArrayListForLoopExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
for (int i = 0; i < list.size(); i++) {
String fruit = list.get(i);
System.out.println(fruit);
}
}
}
增强for循环语法简洁,适用于不需要获取元素索引的情况,示例代码如下:
java
import java.util.ArrayList;
import java.util.List;
public class ArrayListEnhancedForLoopExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
for (String fruit : list) {
System.out.println(fruit);
}
}
}
迭代器可以在遍历过程中安全地删除元素,避免ConcurrentModificationException异常,示例代码如下:
java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ArrayListIteratorExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
if ("banana".equals(fruit)) {
iterator.remove();
}
}
System.out.println("删除后的集合:" + list);
}
}
2.3 ArrayList 扩容机制
-
初始容量与扩容因子 :
ArrayList的默认初始容量为 10,当使用无参构造函数创建ArrayList时,初始的elementData数组为空,但在第一次添加元素时,会将容量扩容为 10。扩容因子默认为 1.5,即每次扩容时,新的容量会是原容量的 1.5 倍。
-
扩容机制 :
当向ArrayList中添加元素时,如果当前元素数量(size)达到了数组的容量(elementData.length),就会触发扩容操作。扩容的核心方法是grow,其实现逻辑如下:
java
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
首先计算新的容量newCapacity,它是原容量oldCapacity加上oldCapacity的一半(右移一位相当于除以 2),即 1.5 倍扩容。然后检查新容量是否小于所需的最小容量minCapacity,如果是,则将新容量设置为minCapacity。接着检查新容量是否超过了最大数组容量MAX_ARRAY_SIZE,如果超过了,则调用hugeCapacity方法来处理超大容量的情况。最后,通过Arrays.copyOf方法将原数组的元素复制到新的、更大的数组中。
- 性能影响 :
扩容过程涉及到内存分配和元素复制,是一个比较耗时的操作,时间复杂度为O(n),其中n是当前数组的大小。频繁的扩容会对性能产生较大影响,因此在使用ArrayList时,如果能够提前预估元素的数量,最好通过构造函数指定初始容量,以减少扩容次数,提高性能。
2.4 ArrayList 线程安全性问题与解决
ArrayList不是线程安全的,当多个线程同时对同一个ArrayList进行操作时,可能会出现数据不一致、ArrayIndexOutOfBoundsException、ConcurrentModificationException等问题。例如,当一个线程在遍历ArrayList,而另一个线程同时进行添加或删除元素的操作时,就可能会抛出ConcurrentModificationException异常。
解决ArrayList线程安全问题的方法有以下几种:
- 使用Collections.synchronizedList :
Collections.synchronizedList方法可以将一个普通的ArrayList包装成一个线程安全的列表。它通过在方法调用上添加synchronized关键字来实现同步,示例代码如下:
java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SynchronizedListExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
List<String> synchronizedList = Collections.synchronizedList(list);
// 多线程操作synchronizedList
new Thread(() -> {
synchronizedList.add("apple");
}).start();
new Thread(() -> {
synchronizedList.add("banana");
}).start();
}
}
在使用Collections.synchronizedList包装后的列表进行迭代时,需要手动同步,以避免并发修改异常,如下所示:
java
synchronized (synchronizedList) {
for (String element : synchronizedList) {
// 处理元素
}
}
- 使用CopyOnWriteArrayList :
CopyOnWriteArrayList是 Java 并发包(java.util.concurrent)中提供的一个线程安全的列表实现,它采用 "写时复制" 的策略。在进行写操作(如添加、删除元素)时,会先复制一份原数组,在新数组上进行修改,然后将原数组的引用指向新数组。而读操作则直接读取原数组,不需要加锁,因此读操作性能极高,适合读多写少的场景。示例代码如下:
java
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
// 多线程操作CopyOnWriteArrayList
new Thread(() -> {
list.add("apple");
}).start();
new Thread(() -> {
list.add("banana");
}).start();
}
}
CopyOnWriteArrayList的缺点是写操作性能较差,因为需要复制整个数组,并且内存占用较大,同时数据具有弱一致性,即读操作可能看不到最新的修改。在实际应用中,我们需要根据具体的业务场景和读写需求来选择合适的解决方案,如果是单线程环境,直接使用ArrayList即可,因为它的性能更高;如果是多线程环境且读写操作频繁,需要根据读写比例来选择Collections.synchronizedList或CopyOnWriteArrayList。
三、LinkedList 集合实战
3.1 LinkedList 底层结构
LinkedList是List接口的另一个重要实现类,它的底层基于双向链表结构。双向链表中的每个节点(Node)包含三个部分:存储的元素(item)、指向前一个节点的引用(prev)和指向后一个节点的引用(next)。在LinkedList类中,维护了链表的头节点(first)和尾节点(last),以及链表的大小(size)。
java
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;
}
}
transient Node<E> first;
transient Node<E> last;
transient int size = 0;
双向链表的工作原理如下:当向LinkedList中添加元素时,如果链表为空,新添加的元素将成为头节点和尾节点,first和last都指向该节点;如果链表不为空,新元素会根据添加位置(头部、尾部或中间)来调整相应节点的指针。例如,在尾部添加元素时,新元素的prev指针指向原尾节点,原尾节点的next指针指向新元素,然后last指针指向新元素。
java
public boolean add(E e) {
linkLast(e);
return true;
}
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++;
}
在删除元素时,首先需要找到要删除的节点,然后调整其前后节点的指针,将该节点从链表中移除。如果删除的是头节点,first指针将指向原头节点的下一个节点;如果删除的是尾节点,last指针将指向原尾节点的前一个节点。由于双向链表的这种结构,它在进行插入和删除操作时,不需要像数组那样移动大量元素,只需要修改指针即可,因此在频繁插入和删除的场景下性能表现较好。但由于它不支持随机访问,通过索引获取元素时需要从头或尾开始遍历链表,时间复杂度为O(n),所以在需要频繁随机访问元素的场景下,LinkedList的性能不如ArrayList。
3.2 LinkedList 常用操作
- 添加元素 :
LinkedList提供了丰富的添加元素方法,如add(E e)将元素添加到链表末尾,addFirst(E e)将元素添加到链表头部,addLast(E e)与add(E e)功能相同,也是将元素添加到链表末尾,add(int index, E element)将元素插入到指定索引位置。
java
import java.util.LinkedList;
public class LinkedListAddExample {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("apple");
list.addFirst("banana");
list.addLast("cherry");
list.add(1, "orange");
System.out.println(list);
}
}
在上述代码中,我们依次使用不同的添加方法向LinkedList中添加元素,最终输出的链表顺序为 "banana"、"orange"、"apple"、"cherry"。
- 删除元素 :
remove()方法移除链表的第一个元素,removeFirst()与remove()功能相同,removeLast()移除链表的最后一个元素,remove(int index)移除指定索引位置的元素,remove(Object o)移除链表中第一个与指定对象相等的元素。
java
import java.util.LinkedList;
public class LinkedListRemoveExample {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
String removedFirst = list.removeFirst();
System.out.println("移除的第一个元素是:" + removedFirst);
String removedLast = list.removeLast();
System.out.println("移除的最后一个元素是:" + removedLast);
boolean removed = list.remove("banana");
System.out.println("是否移除banana:" + removed);
System.out.println("移除后的链表:" + list);
}
}
通过这些删除方法,我们可以方便地从LinkedList中移除指定元素,在实际应用中,根据具体需求选择合适的删除方法可以提高代码的效率和可读性。
- 遍历元素 :
LinkedList可以使用迭代器(Iterator)、增强for循环和普通for循环(不推荐,因为通过索引访问效率低)来遍历。迭代器遍历示例如下:
java
import java.util.Iterator;
import java.util.LinkedList;
public class LinkedListIteratorExample {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
}
}
}
增强for循环遍历示例如下:
java
import java.util.LinkedList;
public class LinkedListEnhancedForLoopExample {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
for (String fruit : list) {
System.out.println(fruit);
}
}
}
与ArrayList相比,LinkedList在添加和删除元素时,由于不需要移动大量元素,只需要修改指针,所以操作更加高效,尤其是在链表中间位置进行插入和删除操作时优势明显。但在遍历元素时,由于LinkedList不支持随机访问,通过索引访问元素需要遍历链表,所以使用迭代器或增强for循环遍历更为合适,而ArrayList可以通过索引快速访问元素,使用普通for循环遍历在某些情况下效率更高。
3.3 LinkedList 与 ArrayList 的性能对比
-
增删改查场景:
- 添加操作 :
- 尾部添加:ArrayList和LinkedList在尾部添加元素时性能都较好。ArrayList在不触发扩容的情况下,时间复杂度为O(1),触发扩容时,由于需要复制数组,时间复杂度为O(n);LinkedList在尾部添加元素,只需要修改指针,时间复杂度始终为O(1)。
- 中间添加:ArrayList在中间位置添加元素时,需要将插入位置及之后的元素向后移动,时间复杂度为O(n);LinkedList在中间位置添加元素,虽然找到插入位置需要遍历链表,时间复杂度为O(n),但找到位置后修改指针的操作时间复杂度为O(1),总体来说,在频繁进行中间位置添加操作时,LinkedList性能更优。
- 删除操作 :
- 头部删除:ArrayList删除头部元素时,需要将后续元素向前移动,时间复杂度为O(n);LinkedList删除头部元素,只需要修改头指针,时间复杂度为O(1),所以LinkedList在头部删除操作上性能更好。
- 中间删除:ArrayList删除中间位置元素,需要移动后续元素,时间复杂度为O(n);LinkedList删除中间位置元素,找到删除位置需要遍历链表,时间复杂度为O(n),找到后修改指针时间复杂度为O(1),在频繁的中间删除操作中,LinkedList更具优势。
- 查询操作 :
- ArrayList支持随机访问,通过索引查询元素的时间复杂度为O(1);LinkedList通过索引查询元素需要从头或尾遍历链表,时间复杂度为O(n),因此在需要频繁查询元素的场景下,ArrayList性能远远优于LinkedList。
- 修改操作 :
- ArrayList通过索引直接定位到元素进行修改,时间复杂度为O(1);LinkedList需要先遍历链表找到要修改的元素,时间复杂度为O(n),找到后修改操作时间复杂度为O(1),所以在频繁修改操作时,ArrayList更高效。
- 添加操作 :
-
适用场景建议:
- 如果应用场景中需要频繁进行随机访问、查询操作,并且插入和删除操作相对较少,应优先选择ArrayList,例如缓存数据的存储,因为缓存数据通常需要快速读取。
- 如果应用场景中频繁进行插入和删除操作,尤其是在链表的头部或中间位置,而对随机访问性能要求不高,LinkedList是更好的选择,比如实现一个简单的消息队列,消息的插入和删除操作频繁,而不需要频繁根据索引获取消息。
3.4 List 集合实战案例(学生信息管理系统初步)
设计并实现一个基于List集合的学生信息管理系统初步版本,该系统可以实现添加学生信息、查询学生信息、删除学生信息等功能。
- 定义学生类:
java
public class Student {
private int id;
private String name;
private int age;
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 实现学生信息管理系统:
java
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class StudentManagementSystem {
private List<Student> students;
public StudentManagementSystem() {
students = new ArrayList<>();
}
// 添加学生信息
public void addStudent(Student student) {
students.add(student);
System.out.println("学生信息添加成功!");
}
// 查询学生信息
public void queryStudent(int id) {
for (Student student : students) {
if (student.getId() == id) {
System.out.println("查询到的学生信息:" + student);
return;
}
}
System.out.println("未找到ID为" + id + "的学生信息。");
}
// 删除学生信息
public void deleteStudent(int id) {
for (int i = 0; i < students.size(); i++) {
if (students.get(i).getId() == id) {
students.remove(i);
System.out.println("学生信息删除成功!");
return;
}
}
System.out.println("未找到ID为" + id + "的学生信息,无法删除。");
}
public static void main(String[] args) {
StudentManagementSystem system = new StudentManagementSystem();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请选择操作:1.添加学生 2.查询学生 3.删除学生 4.退出");
int choice = scanner.nextInt();
switch (choice) {
case 1:
System.out.println("请输入学生ID:");
int id = scanner.nextInt();
System.out.println("请输入学生姓名:");
String name = scanner.next();
System.out.println("请输入学生年龄:");
int age = scanner.nextInt();
Student student = new Student(id, name, age);
system.addStudent(student);
break;
case 2:
System.out.println("请输入要查询的学生ID:");
int queryId = scanner.nextInt();
system.queryStudent(queryId);
break;
case 3:
System.out.println("请输入要删除的学生ID:");
int deleteId = scanner.nextInt();
system.deleteStudent(deleteId);
break;
case 4:
System.out.println("系统退出!");
scanner.close();
System.exit(0);
default:
System.out.println("无效的选择,请重新输入!");
}
}
}
}
在上述代码中,我们使用ArrayList来存储学生信息,通过addStudent方法添加学生,queryStudent方法查询学生,deleteStudent方法删除学生。在main方法中,通过控制台输入来实现与用户的交互,完成学生信息管理的基本功能。如果需要将ArrayList替换为LinkedList,只需要修改StudentManagementSystem类中students的定义为LinkedList即可,代码如下:
java
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class StudentManagementSystem {
private List<Student> students;
public StudentManagementSystem() {
students = new LinkedList<>();
}
// 添加学生信息
public void addStudent(Student student) {
students.add(student);
System.out.println("学生信息添加成功!");
}
// 查询学生信息
public void queryStudent(int id) {
for (Student student : students) {
if (student.getId() == id) {
System.out.println("查询到的学生信息:" + student);
return;
}
}
System.out.println("未找到ID为" + id + "的学生信息。");
}
// 删除学生信息
public void deleteStudent(int id) {
for (int i = 0; i < students.size(); i++) {
if (students.get(i).getId() == id) {
students.remove(i);
System.out.println("学生信息删除成功!");
return;
}
}
System.out.println("未找到ID为" + id + "的学生信息,无法删除。");
}
public static void main(String[] args) {
StudentManagementSystem system = new StudentManagementSystem();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请选择操作:1.添加学生 2.查询学生 3.删除学生 4.退出");
int choice = scanner.nextInt();
switch (choice) {
case 1:
System.out.println("请输入学生ID:");
int id = scanner.nextInt();
System.out.println("请输入学生姓名:");
String name = scanner.next();
System.out.println("请输入学生年龄:");
int age = scanner.nextInt();
Student student = new Student(id, name, age);
system.addStudent(student);
break;
case 2:
System.out.println("请输入要查询的学生ID:");
int queryId = scanner.nextInt();
system.queryStudent(queryId);
break;
case 3:
System.out.println("请输入要删除的学生ID:");
int deleteId = scanner.nextInt();
system.deleteStudent(deleteId);
break;
case 4:
System.out.println("系统退出!");
scanner.close();
System.exit(0);
default:
System.out.println("无效的选择,请重新输入!");
}
}
}
}
通过这种方式,可以方便地切换List集合的实现类,根据实际需求选择更合适的数据结构来优化系统性能。