配色: 字号:
任务8 系统存储结构优化
2022-12-20 | 阅:  转:  |  分享 
  
J 程 序 设 计任务8:系统存储结构优化 目录1、任务描述学生信息管理系统采用数组来存放学生对象,采用数组有一些不够灵活的地方。首先数组
长度是固定的,一旦确定后不允许改变,但实际要管理的学生数目可能是需要随时变化的,如开学时有新生来,毕业时有学生离去。二是数组本身做
插入、删除时涉及大量的元素移动的操作,而且这些操作都需要开发人员自行去实现,因此使用起来不太方便。项目组决定对学生信息管理系统的存
储结构进行优化,不再采用数组,而是采用使用更加方便灵活的集合来实现。要实现本任务,需要了解和掌握Java中各种集合的使用。 技术
准备2.1 Collection接口Java中的集合就像一个容器,专门用来存储Java对象(实际上是对象的引用,但习惯上简称为对
象)。这些对象可以是任意的数据类型,并且长度可变。Java集合中只能存放引用类型的数据,不能存放基本类型数据。集合按照其存储结构可
以分为两大类,单列集合Collection和双列集合Map。Collection是单列集合根接口,用于存储一系列符合某种规则的元素
。Collection集合有两个重要的子接口,分别是List和Set。List集合的特点是元素有序、可重复。该接口的主要实现类有A
rrayList和LinkedList。Set集合的特点是元素无序并且不可重复。该接口的主要实现类有HashSet和TreeSet
。Map是双列集合根接口,用于存储具有键(Key)、值(Value)映射关系的元素。Map集合中每个元素都包含一对键值,并且Key
唯一,在使用Map集合时通过指定的Key找到对应的Value。Map接口的主要实现类有HashMap和TreeMap。2.1 C
ollection接口集合体系结构:这些集合接口及实现类都位于java.util包中,使用时需要导入。2.1 Collectio
n接口Collection接口主要方法:2.2 List接口List接口继承自Collection接口,是单列集合的一个重要分支
,通常将实现了List接口的对象称为List集合(或List列表)。List集合通常用于存放一组可重复、有序(元素有先后次序之分)
的元素,可通过下标来获取某一位置上的元素。List是Collection的子接口,继承了Collection接口的全部方法,同时又
增加了一些自身特有的方法。2.2 List接口List接口主要方法。2.2 List接口1. ArrayListArrayLi
st是List接口的一个实现类,也是程序中经常使用的一种数据结构。在ArrayList内部封装了一个长度可变的数组对象,当存入的元
素超过数组长度时,ArrayList会在内存中分配一个更大的数组来存储这些元素,可以将ArrayList看作是一个长度可变的数组。
通过List接口和Collection接口提供的一些方法可方便地对ArrayList进行操作,如添加、删除、修改元素等。2.2
List接口【例 8?1】ArrayList使用示例2.2 List接口2.2 List接口2.2 List接口2. Lin
kedListLinkedList是List接口的另一个实现类。该集合内部采用一个双向链表来存放元素,双向链表对于节点的增删效率比
用数组要高,因此比较适合需要频繁进行元素增删操作的场合。ArrayList内部是用数组来存放元素,对于元素的查找、遍历比较高效,但
对于元素的增、删则效率相对校低。因此比较适合于元素的查找和遍历的场合。LinkedList集合除了继承自Collection和Li
st接口的方法外,针对元素的增删又定义了一些特有的方法。2.2 List接口LinkedList特有方法。2.2 List接口
【例 8?2】LinkedList常用方法使用示例2.2 List接口2.3 Set接口Set接口和List接口一样,同样继承
自Collection接口。Set集合中存放的是一组无序(没有先后位置之分)、没有重复的元素。Set接口主要有两个实现类:Hash
Set和TreeSet。HashSet是根据元素的哈希值来确定元素在集合中的存储位置,具有良好的存取和查找性能。TreeSet是以
二叉树的方式来存储元素,它可以实现对集合中的元素进行排序。2.3 Set接口Set集合常用方法。2.3 Set接口1. Has
hSetHashSet是Set接口的一个实现类。HashSet集合中存放的元素是无序、唯一的。当向HashSet集合中添加一个元素
时:首先会调用该元素的hashCode()方法来确定元素的存储位置,然后再调用元素对象的equals()方法来确保集合中没有重复元
素。2.3 Set接口【例 8?3】HashSet使用示例提示:集合中只能存放引用类型数据,不能存放基本类型数据。代码中表面看是
往集合中添加了基本类型数据,实际上系统自动将其转换成了相对应的包装类数据,即自动装箱。2.3 Set接口在Java中,一些基本类
型数据包装类、String类等都已经默认重写了hashCode()和equals()方法,因此往HashSet集合中添加这些类型的
元素时,系统能够保证集合中没有重复元素。但对于自定义的数据类型,如用户自定义的类,如果没有重写hashCode()和equals(
)方法则没法实现自动去重功能。2.3 Set接口【例 8?4】在HashSet集合中存入自定义类对象。自定义类没有重写hashC
ode()和equals()方法,所以无法实现自动去重2.3 Set接口【例 8?5】在HashSet集合中存入自定义类对象时实
现自动去重。至于自定义的两个对象如何认定其相同,可以根据实际需要自行定义。本例中只要是两个id(学号)值相同,即认为两个对象是相同
的。2.3 Set接口2. TreeSetTreeSet是Set接口的另一个实现类,它内部采用平衡二叉树来存储元素,来保证Tre
eSet集合中没有重复的元素,并且可以对元素进行排序。二叉树就是每个节点最多有两个子节点的有序树,每个节点及其子节点组成的树称为子
树,左侧的节点称为“左子树”,右侧的节点称为“右子树”,其中左子树上的元素小于它的根结点,而右子树上的元素大于它的根结点。因其存储
结构的特殊性,TreeSet集合本身提供了一些特有方法来操作集合。2.3 Set接口TreeSet集合特有方法:2.3 Set
接口【例 8?6】TreeSet集合使用示例2.3 Set接口在向TreeSet集合中添加元素时,会调用compareTo()方
法进行比较排序,该方法是Comparable接口中定义的,Java的基础类型包装类和String类都实现了Comparable接口
,并默认实现了接口中的CompareTo()方法,因此当往集合中添加这些类型的数据时,系统会自动进行排序。但是当往集合中添加自定义
数据类型数据时,由于这些自定义类型的数据没有实现Comparable接口,因此也就无法直接在TreeSet集合中进行排序操作。为了
解决此问题,Java提供了两种TreeSet的排序规则:自然排序和定制排序。2.3 Set接口(1)自然排序默认情况下,Tree
Set集合都采用自然排序。自然排序要求向TreeSet集合存储的元素所在类必须实现Comparable接口,并重写compareT
o()方法,然后TreeSet集合就会对该类型元素调用CompareTo()方法进行比较,并默认进行升序排序。2.3 Set接口
【例 8?7】自然排序2.3 Set接口(2)定制排序有时候,用户自定义的类型数据所在类没有实现Comparable接口,或者是
实现了Comparable接口,但不想按其定义的CompareTo()方法进行排序。例如对于字符串不想按其事先定义的按英文字母的顺
序进行排序,而是希望按照字符串长度进行排序;或者是对于数值型数据,不想按默认的按数值大小升序排列,想降序排序,这时就可以通过定制排
序来实现。定制排序是在创建TreeSet集合时就自定义一个比较器来对元素进行自定义排序。自定义比较器需要实现Comparator接
口中的compare()方法。2.3 Set接口【例 8?8】对TreeSet集合中字符串按长度进行升序排序因Comparato
r接口是一个函数式接口,因此定制排序也可直接利用Lambda表达式来实现排序规则的定制。2.3 Set接口【例 8?9】对Tre
eSet集合中字符串按长度进行降序排序2.4 Map接口Map接口是一种双列集合,它的每个元素都是一个键值对的组合,键(Key)
和值(Value)之间存在一种对应关系,称为映射。如每个学生都有唯一的学号,一个学号对应一个学生,学号与学生之间就是一对一的关系。
这种映射关系是一对一的,即一个键对应唯一的一个值,其中键和值可以是任意数据类型,键不允许有重复,值可以有重复。如学号与姓名之间,学
号作为键不能有重复值,但姓名作为值可以有重复值。即可能会有同名的学生,但他们的学号是不同的,给定一个学号,只能找到与其对应的一个姓
名。因此在访问Map集合中的元素时,是根据指定的键,来找到其对应的值。2.4 Map接口Map接口中常用的方法:2.4 Map
接口1. HashMapHashMap是Map接口的一个实现类,它用于存储键值对,该集合的键和值允许为空,但键不能重复,且集合中的
元素是无序的。HashMap底层是由哈希表结构组成的,其实就是“数组+链表”的组合体,数组是HashMap的主体结构,链表则主要是
为了解决哈希值冲突而存在的分支结构。正因为这样特殊的存储结构,HashMap集合对于元素的增、删、改、查等操作效率相对较高。2.4
Map接口【例 8?10】HashMap集合使用示例2.4 Map接口2.4 Map接口2. TreeMap集合TreeM
ap是Map接口的另一个实现类。TreeMap底层通过二叉树来存储元素。TreeMap中所有的键是按照某种顺序排列的。2.4 M
ap接口【例 8?11】TreeMpa集合使用示例2.4 Map接口【例 8?12】将TreeMap中元素按键降序排序。2.4
Map接口【例 8?13】利用Lambda表达式实现定制排序,将集合元素按键降序排序。2.5 集合遍历1. Iterator遍
历集合可以利用Collection中的Iterator来遍历集合中的元素。Iterator主要用于迭代访问Collection中的
元素,Iterator对象也被称之为迭代器。使用Iterator遍历集合的主要过程如下。(1)利用集合的iterator()方法获
得迭代器对象。(2)使用迭代器对象的hasNext()方法循环判断集合中是否存在下一个元素,如果存在,则调用其next()方法将元
素取出,否则说明已经到达了集合末尾,停止遍历元素。2.5 集合遍历【例 8?14】Iterator遍历List集合和Set集合。
2.5 集合遍历2.5 集合遍历使用Iterator不仅可以遍历List集合与Set集合,也可以遍历Map集合。但Map集合中
的元素是键值对形式,即有键有值,相当于是两个数据对象,不能直接利用Iterator迭代(每次获得的是一个数据对象),为了能够使用I
terator迭代集合中元素,可以有两种不同的处理方式:一是利用Map集合的keySet()方法得到集合中所有的键(为一Set集合
),然后利用Iterator去迭代键集合,再根据键获取对应的值;二是利用Map集合的entrySet()方法,将整个Map集合转换
为Set集合,Set集合中的每一个元素是一个包含有键和值的整体数据,然后利用Iterator去迭代每个元素,再从每个元素中取出相应
的键和值。2.5 集合遍历【例 8?15】Iterator遍历Map集合。2.5 集合遍历2.5 集合遍历2. forEac
h遍历可利用集合的forEach()方法来遍历集合。forEach()方法的参数是一个函数式接口,可直接利用Lambda表达式来实
现。2.5 集合遍历【例 8?16】forEach遍历集合2.5 集合遍历3. foreach遍历以上两种方法是遍历集合时比较
常用的两种方式。对一些集合的遍历也可以使用前面介绍的foreach循环来实现。2.5 集合遍历【例 8?17】foreach遍历
集合。2.5 集合遍历4. for循环遍历对于List集合,因其是有序集合,可直接利用下标来访问每个元素,因此也可直接利用普通f
or循环通过下标方式遍历。2.5 集合遍历【例 8?18】利用for循环遍历List集合。2.6 泛型集合中可以存储任意类型的
对象元素。但是当把一个对象存入集合后,集合会“忘记”这个对象的类型,当将该对象从集合中取出时,这个对象的编译类型就统一变成了Obj
ect类型。换句话说,在程序中无法确定一个集合中的元素到底是什么类型,那么在取出元素时,如果进行强制类型转换就很容易出错。2.6
泛型【例 8?19】集合中的强制类型转换。当集合中存放的元素类型不一致,在进行强制类型转换时,编译没有问题,但运行时就会出错。2
.6 泛型当集合中存放的元素类型不一致,在进行强制类型转换时,编译没有问题,但运行时就会出错。为了解决此类问题,Java中引入了
“参数化类型(parametered type)”这个概念,即泛型。泛型可以限定操作的数据类型,在定义集合类时,可以使用“<参数化
类型>”的方式指定该集合中存储的数据类型,具体格式如下(以ArrayList集合为例)。ArrayList<参数化类型> list
= new ArrayList<>();通过使用泛型来限定集合中可存储的元素类型后,当集合中类型与声明中的不一样时,就会出现编译
错误,这样就避免了程序在运行时出错。2.6 泛型修改例8-19中的集合声明语句,程序在编译时就会出现错误提示:程序编译报错的原因
是ArrayList限定了集合元素的类型为Integer,因此当往集合中添加String对象时,编译通不过,这样
就可以在编译时解决问题,避免了程序运行时发生异常。2.6 泛型【例 8?20】泛型的使用。使用泛型后,在遍历集合中元素时可以直接
指定元素类型为声明中的类型,而无须再是Object,从而也避免了在程序中进行强制类型转换。2.7 Collections 和Ar
rays工具类1. Collections工具类Collections工具类位于java.util包中,其常用方法如表所示。2.7
Collections 和Arrays工具类【例 8?21】Collections常用方法应用示例2.7 Collectio
ns 和Arrays工具类2.7 Collections 和Arrays工具类2. Arrays工具类Arrays工具类也位于j
ava.util包中,该类提供了若干对数组操作的静态方法,常用方法如表所示。2.7 Collections 和Arrays工具类
【例 8?22】Arrays常用方法应用示例。2.7 Collections 和Arrays工具类2.8 聚合操作在实际开发中
,经常会涉及对集合、数组中元素的操作,在JDK 8之前都是通过普通的循环遍历出每一个元素,然后结合if条件语句选择性地对元素进行查
找、过滤、修改等操作,这种原始的操作方法虽然可行,但是代码量较大并且执行效率较低。为此,JDK 8中新增了一个Stream接口,该
接口可以将集合、数组的中的元素转换为Stream流的形式,并结合Lambda表达式来进一步简化集合、数组中元素的查找、过滤、转换等
操作,这一功能就是聚合操作。根据实际操作流程,聚合操作大体可以分为以下3个过程。(1)将原始集合或者数组对象转换为Stream流对
象;(2)对Stream流对象中的元素进行一系列的过滤、查找等中间操作,然后返回一个Stream流对象;(3)对Stream流进行
遍历、统计、收集等终结操作,获取想要的结果。2.8 聚合操作【例 8?23】聚合操作基本使用示例2.8 聚合操作上述程序的功能
是从一个存放有姓名的集合中找出所有姓王的,然后取前2个输出。对于聚合操作,支持链式调用,即调用有返回值的方法时不获取返回值而是直接
再调用另一个方法,采用此种方式更加简洁、高效。如将上述程序中的聚合操作采用链式调用,修改后的代码如下所示。2.8 聚合操作1.
创建Stream流对象聚合操作针对的就是可迭代数据(如集合、数组等)进行的操作,所以创建Stream流对象其实就是将集合、数组等通
过一些方法转换为Stream流对象。针对不同的数据源,Java提供了多种创建Stream流对象的方式,具体如下。(1)对于Coll
ection单列集合可以使用集合对象的stream()方法获取Stream流对象。(2)对于基本类型包装类数组、引用类型数组可以使
用Stream接口的of()静态方法来获取Stream流对象。(3)对于数组也可以使用Arrays工具类的stream()静态方法
来获取Stream流对象。2.8 聚合操作【例 8?25】创建Stream流对象。2.8 聚合操作2. Stream流常用方法
Java为聚合操作中的Stream流对象提供了非常丰富的操作方法,这些方法被划分为中间操作和终结操作两种类型。这两种类型操作方法的
根本区别就是方法的返回值,只要返回值类型不是Stream类型的就是终结操作,而其它的操作属于中间操作(其返回值仍然是一个Strea
m流对象)。2.8 聚合操作Stream流对象的常用方法:2.8 聚合操作说明:(1)一个Stream流对象可以连续进行多次中
间操作,仍会返回一个流对象,但一个流对象只能进行一次终结操作,并且一旦进行终结操作后,该流对象将不复存在。(2)在进行聚合操作时,
只是改变了Stream流对象中的数据,并不会改变原始集合或数组中的源数据。2.8 聚合操作【例 8?26】从一组姓名中查找以“张
”开头且长度大于2的姓名。2.8 聚合操作【例 8?27】从一组字符串中查找包含“a”且长度大于4的字符串,将其全部转换成大写。
2.8 聚合操作【例 8?28】将一组整数去重后升序排序,然后取其第2~5个元素。2.8 聚合操作【例 8?29】将一组整数中
所有的偶数收集到列表中,所有的奇数收集到字符串中,字符串中要求用-分隔各个数字。2.8 聚合操作【例 8?30】统计一组字符串中
以“a”开头的字符串的个数。 任务实施3.1 存储结构优化本任务需要将已有的学生信息管理系统中的存储结构数组换用一种更加方便灵活
的数据结构来存放学生对象,然后基于新的数据存储结构实现学生信息的添加、删除、修改、查找和显示等功能。经过综合考虑,项目组决定采用A
rrayList集合做为学生信息管理系统的数据结构。采用ArrayList集合存放学生对象,可方便地进行学生对象的添加、删除、修改
及查找功能。因为ArrayList集合本身已经提供了相应的元素的添加、删除、修改等操作,无须再自行实现,因此原系统设计模式中的控制
层无须再自行实现(即StudentList类不再需要),只需要模型类(Student类)和视图类(StudentView类)即可。
模型类不需要改变,将视图类基于新的存储结构重新实现学生信息的添加、删除、修改、显示和查找功能。视图类中功能菜单的显示、菜单处理及成
绩异常处理不需要改变,即原StudentView类中的printMenu()方法、process()方法、enterScore()
方法和main()方法不需要改变。3.1 存储结构优化视图类StudentView中采用ArrayList集合来存放学生对象,要使
用ArrayList,需要先导入相应的ArrayList,导入代码如下。import java.util.ArrayList;将原StudentView类中用于存放学生对象的数组替换成ArrayList集合,修改后的代码如下。private ArrayList stuList = new ArrayList<>();3.2 基于新存储结构的学生信息添加在添加学生信息时同样需要先判断一下用户输入的学号是否已经存在,如果已经存在则给出相应的提示,如果不存在,则添加相应的学生信息。判断学号是否存在不再使用原来的find()方法,在StudentView类中重新定义一个idExists()方法用于判断学号是否存在,代码如下:3.2 基于新存储结构的学生信息添加添加学生信息的add()方法修改为如下:3.3 基于新存储结构的学生信息删除删除学生信息的delete()方法修改为:3.4 基于新存储结构的学生信息修改修改学生信息的modify()方法修改为如下:3.5 基于新存储结构的学生信息显示显示学生信息的show()方法修改为如下:3.6 基于新存储结构的学生信息查找 按姓氏进行模糊查找的showByName()方法修改为如下:3.7 系统测试运行程序,测试系统功能,学生信息添加和显示及查找功能测试结果如下所示:3.7 系统测试学生信息修改和删除功能测试结果:J 程 序 设 计谢 谢
献花(0)
+1
(本文系籽油荃面原创)