p90 集合基础
什么是集合?
集合是一个与数组类似的容器
为什么要学习集合?
数组定义完成并启动后长度会固定;而集合大小可变,开发中用的更多。
集合可变的原理?
这里以ArrayList为例:
创建一个ArrayList集合容器时,会在底层创建一个长度为10的空数组(ArrayList类的无参构造方法确定的长度),随后向里存入数据;在某一时刻填入数据时若数组已经为满,则会创建一个新的数组,长度为旧数组的1.5倍,随后先把旧数组的数据拷贝到新数组里,再把要添加的新数据添加到新数组里,而旧数组会被添加到内存回收器进行清理以腾出空间。
如何选用数组还是集合?
数组适合存储的元素个数不会变动的情况,集合适合存储的元素个数经常发生改变的情况
p91 集合的创建和添加
这里以前面提到过的ArrayList类为例来讲述集合的相关操作。
首先由api文档可见,ArrayList类不属于java.lang包,使用时需要import java.util.ArrayList;
如果使用一般的构造方法创建集合,也就是直接使用ArrayList()方法创建后,该集合中可以存放任意类型的数据,但是这一操作不严谨,可能会造成诸多问题,所以通常都要对集合中存入的数据进行类型限制,限制方法为在类名和方法名的后面加一个尖括号。jdk7版本开始后面的尖括号可以不填内容,它会自动与前面的尖括号的内容进行匹配,但是尖括号本身还是需要存在的。
示例 创建一个只允许存放字符串类型的ArrayList类型集合:
ArrayList<String> list = new ArrayList<String>();
这个尖括号叫做泛型。在现在的情况下只需要知道它可以对集合中存储的数据进行类型限制。
注:泛型中不允许编写基本数据类型。那么如果需要让它限定集合只能输入基本数据类型该怎么做?使用基本数据类型所对应的包装类。
八种基本数据类型对应的包装类怎么记忆?
除了int对应的包装类是Integer,char对应的是Character以外,其余六种基本数据类型对应的包装类都是将它们首字母大写,例如byte对应Byte。
add()子方法可以用来给集合中添加数据。在限制了数据类型的集合中,使用add()方法添加不符合数据类型的数据会报错。泛型是一个很强势的限制方法,它不会调用任何转换(隐式或强制转换)来允许数据类型不符合的数据被加入。
p92 集合常用成员方法
add()方法:
向集合的末尾添加数据,有两种格式:
只有一个参数的情况下,传入的参数就是添加的数据;
有两个参数的情况下,后一个参数是添加的数据,前一个参数是int类型,代表将后面的数据添加到集合中的哪一个特定索引位置。
该方法有一个boolean类型的返回值,返回的是添加数据操作本身是否添加成功。对于ArrayList这个类来说,它的添加方法不会失败,所以这里可以将它忽略。
remove()方法:
删除集合中特定情况的元素,有两种格式:
带有一个int类型的参数,效果为移除集合中这个参数作为索引的对应位置上的数据。这个方法有返回值,返回值的内容是这个方法删除的内容;
带有一个对象作为参数,效果为在这个集合中搜索这个对象含义相同的数据。有一个boolean类型的返回值,返回值的内容是是否查找并删除了对应的对象。
set()方法:
用指定的元素替换此集合中特定索引位置上的元素:
带有一个int类型的参数和一个与集合内容对应类型的参数,前者是替换数据的索引位置,后者是替换的新的数据。有一个返回值,返回的是被替换掉的数据。
上面三个方法的返回值在使用上基本不做接收。
get()方法:
传入一个int类型的参数,效果为查找集合在这个参数的索引位置上的数据并返回,返回值是被查找到的数据。
size()方法:
无参方法,有一个int类型的返回值,返回值是这个集合里拥有的元素数量。
p93 集合的遍历
练习1:集合存储字符串并遍历
需求:创建一个存储字符串的集合,内部存储3个字符串元素,使用程序实现在控制台遍历该集合。
用循环结构加前面的get()方法就能做,这里不加展示
练习2
需求:创建一个存储字符串的集合,内部存储几个长度不同的字符串元素,将四个字长度的元素打印出来。
在循环构造中加入了一个if语句用来判断元素长度,若为4则打印。
练习结束后开始为后面的学生管理系统做铺垫。
创建一个domain包用于存放学生的JavaBean,创建Student的JavaBean,暂时只包含姓名和年龄数据并封装成JavaBean,然后创建一个存储学生对象的集合,并存储三个学生对象,最后进行遍历输出。
public static void main(String[] args) { //创建三个学生对象 Student stu1 = new Student("张三",18); Student stu2 = new Student("李四",19); Student stu3 = new Student("王五",21); //创建集合 ArrayList<Student> stuList = new ArrayList<>(); //把三个学生对象加进集合 stuList.add(stu1); stuList.add(stu2); stuList.add(stu3); //遍历输出学生 for (int i = 0; i < stuList.size(); i++) { //这里自动调用了JavaBean里的toString方法,所以会打印学生的数据而不是给出地址 System.out.println(stuList.get(i)); } }
p94 键盘录入学生信息到集合
以上一p写的存储学生信息部分为基础,修改录入部分,使得学生信息由键盘输入。
把填入学生信息的部分使用scanner改成由键盘输入信息的形式,并将添加学生信息的整个部分提取,作为一个方法独立出来,每次需要添加学生就调用这个方法。
private static void addStu(ArrayList<Student> stuList) { Scanner sc = new Scanner(System.in); System.out.println("请输入学生姓名"); String name = sc.nextLine(); System.out.println("请输入学生年龄"); int age = sc.nextInt(); Student stu = new Student(name,age); stuList.add(stu); }
调用一次该方法,内存中发生的情况如下:
main方法调用addStu方法,该方法进入栈内存;
scanner sc 在栈内存的该方法中建立一个Scanner类的sc对象,new Scanner在堆内存中开辟一块空间,随后把这块空间的地址返回给sc;
输出"请输入学生姓名",String name在栈内存方法区中建立一个String类的对象name,在堆内存中开辟一块空间,sc.nextLine()获取键盘输入并把这块数据交给name的空间,随后把这块空间的地址返回给name;
年龄部分同上,只有数据类型不同;
Student stu在方法区中建立一个Student类对象stu,new Student(name,age)在堆内存中开辟一块空间,将name和age里包含的数据传递给这块空间,随后把这块空间的地址返回给stu;
stuList.add(stu)将stu的地址添加给堆内存的stuList部分,使得集合可以通过地址找到这个新加入的学生对象的地址并且调用里面的数据。
p95 集合删除和筛选数据
练习1:给定一个有几个元素的集合,删除里面的所有"test",并且打印剩下的元素。
思路:遍历整个集合,每当检测到有要删除的数据的时候用remove(int index)方法移除这个位置的数据。
补充1:用变量与常量使用equals()方法进行对比时,最好使用常量的equals方法传入变量进行判断,因为空对象的equals()方法会引起空指针异常,而前后交换保证不会引用空对象的equals()方法就不会出现该问题。
补充2:进行删除时对集合进行遍历,检测到目标后使用remove()时候要注意,remove方法会将删除的数据后方的数据前移以补上这个被删除的空缺,如果不把遍历的索引调回一位的话,新顶上这个空位的那个数据会被从查找中跳过。通常的解决方法有两种,一种是在检测到目标、进行完删除操作之后将循环的索引调回一位,另一种方法是从后向前进行遍历,这样可以防止数据的索引变动导致的数据漏过筛查。
例如,一个集合,内部有"aaa","bbb","bbb"三个元素,需要遍历并且删除"bbb"元素的情况下,如果只用正常循环和搜查删除操作,在搜查到第一个"bbb"时候将其删除,后面的另一个相同的元素就会补上它的空缺,但是这个位置刚刚被筛查过,如果循环不回头(也就是不把搜查的索引调回一位),这个本应被删除的元素就会被跳过,最终的结果就不会符合要求。
练习2:定义一个方法,方法接收一个集合对象,方法内部将传入的集合中年龄低于18的学生对象找出并存入新的集合对象,最后方法返回新的集合。
思路:创建新集合,遍历旧集合,查找到有年龄小于18的student对象就将这个对象添加给新的集合,最后返回新集合。
p96 学生管理系统成品演示
一个完整的学生管理系统:
拥有四个功能,对应增删改查;
学生有四个信息,分别是学号、姓名、年龄、生日;
学号是唯一的,不允许重复;修改、删除学生信息时使用学生学号进行查找和对应操作;
查看学生信息功能打印所有学生信息;
p97-p100 学生管理系统功能模块编写
一路顺畅,系统整体没问题,但是有几个可以提升或者要注意的点:
1.在退出整个系统的时候不仅可以使用break配合循环的标号来实现,还可以使用System.exit(0)方法,该方法的效果是停止正在运行的jvm虚拟机以达成退出整个系统的效果。
2.在查看学生信息的部分的打印语句中,善用\t(制表符)可以使得输出之后的结果更加平整,每个表头后面需要的制表符数量根据打印出来的数据长度来调整,打印出来越长、需要的制表符越多。在该示例中,学号后使用了三个制表符,另外四个部分都使用了一个,如图:
打印结果示例如下:
本章学习目标:
- 能够使用集合存储元素,并完成元素的增删改查
- 能够熟练掌握集合的遍历(独立完成课上案例)
- 能够独立完成学生管理系统
这些向上的以前的所有内容,都能弄懂、搞明白,就算是javaSE这玩意开始正式入门了,
大的要来了)