一、集合框架
1、单列集合Collection
集合的内部很多利用的就是数组,主要是为了方便数据的增删改查。
- 集合只能存储引用类型的数据。
- 引用类型变量实际上存储的是对象的地址,集合中实际上只存储了元素对象在堆中的地址。
- 单列集合Collection接口有List、Set、Queue三个子接口。
Collection接口的主要方法
add()方法示例:
/**
* @document: add()方法 往集合中添加一个元素
* @Author:SmallG
* @CreateTime:2023/8/4+14:21
*/
public class Demo01 {
public static void main(String[] args) {
//创建集合对象 接口不能实例化,需要实现类
Collection c = new ArrayList();
System.out.println(c); //新创建的集合默认是空的
//往集合中添加数据
c.add("SmallG");
c.add("SmallY");
c.add("666");
System.out.println(c);
}
}
contains方法示例:
用于判断给定的元素是否被包含在集合中。
/**
* @document: contains()方法用于判断一个集合中是否包含一个元素
* @Author:SmallG
* @CreateTime:2023/8/4+14:27
*/
public class Demo02 {
public static void main(String[] args) {
Collection c = new ArrayList();
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); //比较的是引用类型的地址 //false
c.add(str1);
c.add(str2);
String str3 = new String("Hello");
//contains()方法是基于对象的equals()方法的比较
System.out.println(c.contains(str3)); //true
System.out.println(c.contains("hello"));
}
}
size、clear、isEmpty方法:
/**
* @document: collection集合常用方法:
* size() 返回集合中元素的个数
* clear() 清空集合
* isEmpty() 判断集合是否为空
* @Author:SmallG
* @CreateTime:2023/8/4+14:39
*/
public class Demo03 {
public static void main(String[] args) {
Collection c = new ArrayList();
System.out.println(c.isEmpty()); //true
c.add("Java");
c.add("C++");
c.add("C#");
c.add("PHP");
c.add("Python");
System.out.println(c.isEmpty()); //false
//输出集合的大小
System.out.println(c.size()); //5
//清空集合
c.clear();
System.out.println(c);
}
}
addAll与ContainsAll方法:
/**
* @document: Collection的常用方法
* addAll() 把一个集合中的所有元素添加到另外一个集合中
* containsAll() 判断一个集合中是否包含另外一个集合中所有的元素
* @Author:SmallG
* @CreateTime:2023/8/4+15:03
*/
public class Demo04 {
public static void main(String[] args) {
//考试通过的学生名单
Collection c = new ArrayList();
c.add("SmallG");
c.add("SmallY");
c.add("SmallSmallG");
//再创建一个集合
Collection a = new ArrayList();
a.add("SmallG");
a.add("SmallY");
//判断a组同学是否都通过了考试
boolean pass = c.containsAll(a);
if (pass) {
System.out.println("A组全部通过考试");
} else {
System.out.println("A组没有全部通过考试");
}
//创建b组学生名单
Collection b = new ArrayList();
b.add("SmallL");
b.add("SmallX");
//把b组所有学生添加到c组学生名单中
c.addAll(b);
System.out.println(c);
}
}
2、Iterator迭代器
用于遍历集合中的所有元素,它的方法有:
- hasNext():用于判断集合中是否有更多元素
- next():返回迭代器中的下一个元素
- remove():从集合中删除最近一次调用next()方法返回的元素
- **遍历时:Collection的remove()方法不能直接删除集合元素,否则会抛出并发更改异常
/**
* @document: 迭代器遍历集合
* 用于遍历集合
* hasNext() 判断迭代器中是否含有下一个元素
* Next() 方法取出下一个元素
* @Author:SmallG
* @CreateTime:2023/8/4+15:22
*/
public class Demo05 {
public static void main(String[] args) {
Collection c = new ArrayList();
// 往集合中存入数据
for (int i = 1; i <= 10; i++) {
c.add(i);
}
System.out.println(c);
//遍历集合 获取集合的迭代器对象
Iterator iterator = c.iterator();
//通过迭代器去遍历集合
while (iterator.hasNext()) {
int i = (int) iterator.next(); //需要强制类型转换
System.out.print(i + " ");
//通过迭代器删除集合中的元素
if (i % 2 == 0) {
iterator.remove();
}
}
System.out.println();
System.out.println(c);
}
}
增强for循环
增强for循环缺点:无法确定遍历的下角标。
/**
* @document: 增强for循环
* @Author:SmallG
* @CreateTime:2023/8/4+15:39
*/
public class Demo06 {
public static void main(String[] args) {
Collection c = new ArrayList();
//往集合中添加数据
for (int i = 1; i <= 10; i++) {
c.add(i);
}
//增强for循环遍历集合
for (Object o : c) {
System.out.print(o + " ");
}
System.out.println();
//增强循环遍历数组
String[] arrs = {"Tom", "SmallG", "Jerry"};
for (String s : arrs) {
System.out.println(s);
}
}
}
3、泛型机制
泛型的本质是数据类型的参数化
/**
* @document: 泛型
* @Author:SmallG
* @CreateTime:2023/8/4+16:01
*/
public class Demo07 {
public static void main(String[] args) {
//创建一个集合 创建的时候添加泛型 使用泛型约束集合中元素的数据类型
Collection<Integer> c = new ArrayList();
c.add(80);
c.add(85);
c.add(70);
//使用迭代器
Iterator<Integer> iterator = c.iterator();
while (iterator.hasNext()) {
int i =iterator.next();
System.out.println(i);
}
}
}
二、List集合
1、List集合
List接口直接继承Collection接口,用于定义线性表数据结构,允许出现重复的元素
方法声明 | 功能描述 |
---|---|
void add(int index,E element) | 在列表的指定索引位置插入指定元素 |
boolean addAll(int index,Collection<? extends E> e) | 在列表的指定索引位置插入集合e的所有元素 |
E get(int index) | 返回列表中指定索引位置的元素 |
int indexOf(Object o) | 返回列表中第一次出现指定元素的索引,若不包含该元素则返回-1 |
int lastIndexOf(Object o) | 返回列表中最后出现指定元素的索引,若不包含该元素则返回-1 |
E remove(int index) | 移除指定索引位置上的元素 |
E set(int index,E element) | 用指定元素替换列表中指定索引位置的元素 |
ListIterator | 返回列表元素的列表迭代器 |
ListIterator | 返回列表元素的列表迭代器,从指定索引位置开始 |
List | 截取列表元素 |
default void sort(Comparator<?super E> e) | 根据指定的比较器规则对集合元素进行排序 |
Object[] toArray() | 将集合元素转换为数组 |
2、ArrayList
list接口的实现类,内部封装了一个长度可变的数组对象,当元素存入的数据超过数组长度,自动扩容。
特点:
- 动态大小:自动调整空间大小
- 随机访问:通过索引访问
- 允许重复元素
- 支持动态修改:可以添加、删除、插入、替换
- 迭代和遍历:都支持。
ArrayList常用方法
void add(int index,E element):在列表的指定索引位置插入指定元素
remove(int index):删除指定位置的元素,并将该元素返回
set(index,Element):修改指定位置的数据
E get(int index):获取集合中指定指定位置的元素
E get(int index,E element):将给定元素存入指定位置,并将原位置的元素返回
size():获取集合中数据的个数
clear():清空集合
isEmpty():判断集合是否为空
/**
* @document: Arraylist集合
* @Author:SmallG
* @CreateTime:2023/8/4+16:13
*/
public class Demo08 {
public static void main(String[] args) {
//创建ArrayList集合对象 使用泛型
ArrayList<String> myList = new ArrayList<>();
//往集合中添加元素
myList.add("one");
myList.add("two");
myList.add("three");
System.out.println(myList);
System.out.println("--------------------");
//取出集合中指定位置的元素
String s = myList.get(1);
System.out.println("索引为1的元素是:" + s);
System.out.println("--------------------");
//替换集合中某一位置中的数据 ,返回被替换的数据
String old = myList.set(0, "SmallG");
System.out.println("被替换的数据是:" + old);
System.out.println("集合中的数据有::" + myList);
System.out.println("--------------------");
//删除集合中的数据
String delete = myList.remove(1);
System.out.println("被删除的数据是:" + delete);
System.out.println("集合中的数据有::" + myList);
System.out.println("--------------------");
//取出集合中的一部分
List<String> subList = myList.subList(0, 2);//包括0但不包括2
System.out.println("获取的子集是:" + subList);
System.out.println("--------------------");
//增强for循环(foreach)遍历集合
for (String str : myList) {
System.out.print(str + " ");
}
System.out.println();
}
}
ArrayList的扩容机制
(1)初始容量
初始容量两种情况:未指定初始长度和手动指定初始长度。
ArrayList初始容量为0,在添加第一个元素时动态扩容为10
手动指定初始长度:通过带参构造器来创建ArrayList对象,如果手动指定了长度,ArrayList的初始容量即为指定的长度,一般情况下,都会用未指定初始长度的ArrayList。
(2)容量增长
当添加元素导致ArrayList长度超过本身的长度时,ArrayList会自动进行容量增长,容量的增长策略是通过创建一个新的更大的数组,并将原有元素复制到新数组中。
(3)复制元素
- 在进行容量增长时,ArrayList会创建一个新的数组,并将原有的元素复制到新数组中
return elementData = Arrays.copyOf(elementData, newCapacity);
- 复制元素的操作可能会导致一定的性能开销,特别是存储大量元素的时候,当事先知道要存储的总元素数量时,应使用ArrayList的带参构造器来创建ArrayList,以减少动态扩容带来的性能开销(事实项目要先完成功能,再考虑优化性能)
(4)ArrayList缩容操作
需要手动缩容trimToSize(),根据项目需要进行操作。
ArrayList与数组的转换
ArrayList集合的toArray()方法用于将集合转换为数组(不常用)
注意元素类型要保持一致。
/**
* @document: ArrayList集合与数组的相互转换 Ai生成
* @Author:SmallG
* @CreateTime:2023/8/7+9:45
*/
public class Demo10 {
public static void main(String[] args) {
// 创建一个ArrayList集合
ArrayList<String> arrayList = new ArrayList<>();
// 添加元素到ArrayList
arrayList.add("Apple");
arrayList.add("Banana");
arrayList.add("Orange");
arrayList.add("Grapes");
// 将ArrayList转换为数组
String[] array = arrayList.toArray(new String[arrayList.size()]);
// 输出数组元素
System.out.println("数组元素:");
for (String item : array) {
System.out.println(item);
}
// 创建一个新的数组
String[] newArray = {"Pineapple", "Watermelon"};
// 将数组转换为ArrayList
ArrayList<String> newArraylist = new ArrayList<>(Arrays.asList(newArray));
// 输出ArrayList元素
System.out.println("ArrayList元素:");
for (String item : newArraylist) {
System.out.println(item);
}
}
}
3、LinkedList
LinkedList概述
线性列表集合,内部维护了一个双向链表,适合于增和删操作,查询效率低。
LinkedList特点
- 链表结构
- 动态大小,根据需要自动扩展大小
- 高效插入和删除
- 不支持随机访问
- 遍历操作:可以用迭代器
ArrayList和LinkedList比较示例:
/**
* @document: LinkedList与ArrayList的集合对比
* @Author:SmallG
* @CreateTime:2023/8/7+10:22
*/
public class Demo11 {
public static void main(String[] args) {
//定义一个次数 执行十万次迭代
int iteration = 1000000;
//创建ArrayList集合
ArrayList<Integer> arrayList = new ArrayList<>();
long arrayListStartTime = System.currentTimeMillis();
//往ArrayList集合中插入数据,从头部插入
for (int i = 0; i < iteration; i++) {
arrayList.add(0, i);
}
long arraylistEndTime = System.currentTimeMillis();
long arraylistDuration = arraylistEndTime - arrayListStartTime;
System.out.println("ArrayList头部插入耗时:" + arraylistDuration);
//创建LinkedList
LinkedList<Integer> linkedList = new LinkedList<>();
//开始时间戳
long linkedListStartTime = System.currentTimeMillis();
for (int i = 0; i < iteration; i++) {
linkedList.addFirst(i);
}
long linkedListEndTime = System.currentTimeMillis();
long linkedListDuration = linkedListEndTime - linkedListStartTime;
System.out.println("LinkedList头部插入耗时:" + linkedListDuration);
System.out.println("-------------------------");
//清空集合,向中间插入数据时linkedList插入效率大大降低
// 因为LinkedList需要遍历增加了耗时
arrayList.clear();
linkedList.clear();
arrayListStartTime = System.currentTimeMillis();
//往中间插入数据
for (int i = 0; i < iteration; i++) {
arrayList.add(i / 2, i);
}
arraylistEndTime = System.currentTimeMillis();
arraylistDuration = arraylistEndTime - arrayListStartTime;
System.out.println("ArrayList中间插入耗时:" + arraylistDuration);
linkedListStartTime = System.currentTimeMillis();
for (int i = 0; i < iteration; i++) {
linkedList.add(i / 2, i);
}
linkedListEndTime = System.currentTimeMillis();
linkedListDuration = linkedListEndTime - linkedListStartTime;
System.out.println("LinkedList中间插入耗时:" + linkedListDuration);
System.out.println("-------------------------");
//尾部添加元素
arrayList.clear();
linkedList.clear();
arrayListStartTime = System.currentTimeMillis();
for (int i = 0; i < iteration; i++) {
arrayList.add(i);
}
arraylistEndTime = System.currentTimeMillis();
arraylistDuration = arraylistEndTime - arrayListStartTime;
System.out.println("ArrayList尾部添加元素耗时:" + arraylistDuration);
linkedListStartTime = System.currentTimeMillis();
for (int i = 0; i < iteration; i++) {
linkedList.addLast(i);
}
linkedListEndTime = System.currentTimeMillis();
linkedListDuration = linkedListEndTime - linkedListStartTime;
System.out.println("LinkedList尾部添加元素耗时:" + linkedListDuration);
}
}
三、练习
1 重构学生平时分记录程序
请基于所学内容,重构学生平时分记录程序,在原有需求的基础上,满足以下需求:
1、设计一个实体类Record封装每次加分或减分记录。
2、采用集合存储所有的记录。
3、实现数据的持久化存储,包括:
- 启动程序时,从本地文件加载之前保存的记录;
- 退出程序前,将记录写入本地文件,覆盖之前保存的记录。
提示:Java支持集合对象的序列化和反序列化操作,但需要注意,集合中存储的类型也需要实现Serializable接口。
程序运行效果参考《学生平时分记录程序_需求规格说明书》。
更新后的程序主流程图如下所示。
package day01.homework;
import java.io.Serializable;
/**
* @document: 平时分明细
* @Author:SmallG
* @CreateTime:2023/8/4+18:45
*/
public class Score implements Serializable {
private String name; //姓名
private String operate; //操作
private Double score; //本次加分
private Double lastScore; //最后剩下的分
private String description; //说明
@Override
public String toString() {
return name + "," +
operate + "," +
score +"," +
lastScore +"," +
description;
}
public Score() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getOperate() {
return operate;
}
public void setOperate(String operate) {
this.operate = operate;
}
public Double getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
public Double getLastScore() {
return lastScore;
}
public void setLastScore(Double lastScore) {
this.lastScore = lastScore;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Score(String name, String operate, Double score, Double lastScore, String description) {
this.name = name;
this.operate = operate;
this.score = score;
this.lastScore = lastScore;
this.description = description;
}
}
package day01.homework;
import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;
/**
* @document: IO流,主要用于存储
* @Author:SmallG
* @CreateTime:2023/8/4+18:51
*/
public class IoStorage {
static int count = 0;
static double lastScore = 90.0;
//存储信息
public static boolean saveInfo(String path, ArrayList<Score> scores) {
boolean flag = false;
try (
FileOutputStream fos = new FileOutputStream(path);
ObjectOutputStream oos = new ObjectOutputStream(fos)
) {
oos.writeObject(scores);
count++;
if (count>=6){
flag = false;
}else{
flag = true;
}
} catch (IOException e) {
e.printStackTrace();
}
return flag;
}
//读取信息
public static ArrayList<Score> readScore(String path) {
File file = new File(path);
// 如果文件不存在,则返回一个空集合
if (!file.exists()) {
return new ArrayList<Score>();
}
try (
FileInputStream fis = new FileInputStream(path);
ObjectInputStream ois = new ObjectInputStream(fis)
) {
ois.read();
return (ArrayList<Score>) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//设置存储位置
String path = "src/day01/homework/scores.csv";
ArrayList<Score> scores = new ArrayList<>();
boolean flag = true;
System.out.println("请输入学生姓名:");
String name = scanner.next();
while (flag) {
System.out.println("----------学生平时分记录程序----------");
System.out.println(" 1 评分明细");
System.out.println(" 2 登记加分");
System.out.println(" 3 登记减分");
System.out.println(" 4 退 出");
System.out.println();
System.out.println(" 请选择(1-4):");
int n = scanner.nextInt();
switch (n) {
case 1:
System.out.println("-------------------当前平时分明细记录-------------------");
System.out.println("姓名\t\t操作\t\t本次分数\t\t当前平时分\t\t说明");
scores = readScore(path);
for (Score score : scores) {
System.out.println(score.getName()+"\t\t\t"+
score.getOperate()+"\t\t"+
score.getScore()+"\t\t\t"+
score.getLastScore()+"\t\t\t"+
score.getDescription()+"\t");
}
break;
case 2:
if (count <= 5) {
System.out.println("本次加分分值:");
double sc = scanner.nextInt();
System.out.println("本次加分说明:");
String s1 = scanner.next();
scores.add(new Score(name,"加分",sc,lastScore+sc,s1));
boolean flag1 = saveInfo(path, scores);
if (flag1) {
System.out.println("加分登记成功");
}
} else {
System.out.println("记录已满,请清理后再进行登记!");
}
break;
case 3:
if (count <= 5) {
Score score = new Score();
System.out.println("本次减分分值:");
double num = scanner.nextInt();
score.setName(name);
score.setOperate("减分");
score.setScore(num);
score.setLastScore(lastScore - num);
System.out.println("本次减分说明:");
String s1 = scanner.next();
score.setDescription(s1);
scores.add(score);
boolean flag1 = saveInfo(path, scores);
if (flag1) {
System.out.println("减分登记成功");
}
} else {
System.out.println("记录已满,请清理后再进行登记!");
}
break;
case 4:
System.out.println("确认是否退出(Y/N):");
String s = scanner.next();
if (s.equals("y") || s.equals("Y")) {
System.out.println("谢谢使用,再见!");
flag = false;
break;
} else if (s.equals("n") || s.equals("N")) {
System.out.println("退出取消,继续使用");
} else {
System.out.println("输入字符有误!");
}
break;
default:
System.out.println("输入序号有误!");
break;
}
}
}
}
2 分解质因数
将一个用户输入的正整数分解质因数。例如:输入90,打印输出90=2×3×3×5。
提示:可以考虑使用递归的思想实现。
程序运行效果如下所示:
/**
* @document: 质因子
* @Author:SmallG
* @CreateTime:2023/8/5+11:47
*/
public class PrimeFactor {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个正整数:");
//输入
int n = scanner.nextInt();
System.out.println("n = " + n);
prime(n);
for (int i = 0; i < arrayList.size(); i++) {
System.out.println("质因子:" + arrayList.get(i));
}
System.out.print("n = ");
for (int i = 0; i < arrayList.size(); i++) {
if (i == 0) {
System.out.print(arrayList.get(i));
} else {
System.out.print("*" + arrayList.get(i));
}
}
}
static ArrayList<Integer> arrayList = new ArrayList<>();
public static ArrayList<Integer> prime(int n) {
if (n % 2 == 0) {
n = n / 2;
arrayList.add(2);
prime(n);
} else if (n % 3 == 0) {
n = n / 3;
arrayList.add(3);
prime(n);
} else if (n % 5 == 0) {
n = n / 5;
arrayList.add(5);
prime(n);
} else if (n == 1) {
} else {
arrayList.add(n);
}
return arrayList;
}
}