1 问题分析和任务定义
1.1 问题描述和要求
【问题描述】为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,各参赛队进场比赛时间可设为0.5秒)
【基本要求】界面要求:交互设计要合理,每个功能可以设计菜单,用户根据提示,完成相关功能的要求。
1.2 问题分析
根据题目要求可知,主要的步骤如下
- 实现对参赛队伍的分类
- 将分类好的队伍放入对应的决赛室中
- 模拟叫号程序,每隔0.5秒进行下一次叫号
2 数据结构的选择和概要设计
2.1 数据结构的选择
选择了List,Map,与HashMap
Map 提供了一个更通用的元素存储方法。Map 集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值。从概念上而言,可以将 List 看作是具有数值键的 Map。
Map提供接口分别用于返回 键集、值集或键-值映射关系集。entrySet()用于返回键-值集的Set集合;keySet()用于返回键集的Set集合;values()用户返回值集的Collection集合,因为Map中不能包含重复的键;每个键最多只能映射到一个值。所以,键-值集、键集都是Set,值集时Collection。
Map提供了“键-值对”、“根据键获取值”、“删除键”、“获取容量大小”等方法。
AbstractMap 是继承于Map的抽象类,它实现了Map中的大部分API。其它Map的实现类可以通过继承AbstractMap来减少重复编码。
HashMap 继承于AbstractMap,但没实现NavigableMap接口;因此,HashMap的内容是“键值对,但不保证次序”!
Map接口的方法如下:
+clear():void 从映射表中删除所有的条目
+containsKey(key:Object):boolean 如果包含了指定键的条目,则返回true
+containsValue(value:Object):boolean 如果该映射表将一个或者多个键映射到指定值,返回true
+entrySet():Set<Map.Entry<K,V>> 返回一个包含了该映射表中条目的集合
+get(key:Object):V 返回指定键对应的值
+isEmpty():boolean 判断映射表是否为空
+keySet():Set<K>返回一个包含该映射表中所有键的集合
+put(key:K,value:V):V将一个条目放入该映射表中
+putAll(m:Map<? extends K,?extends V>): void 将m中所有条目添加到该映射表
+remove(key:Object):V删除指定键对应的值
+size():int返回映射表中的条目数
+values():Collection<V>返回该映射表中所有值组成的集合
方法entrySet()返回一个所有条目的集合,这些条目是Map.Entry<K,V>接口的实例,这里的Entry是Map内部的一个接口。其方法如下:
+getKey():K 返回该条目的键
+getValue():V 返回该条目的值
+setValue(value:V):void 将该条目的值赋以新的值
对于定位一个值,插入一个条目以及删除一个条目而言,HashMap类是高效的。
本实验的编程语言采用 Java ,模仿赛事系统,进行设计,功能包括文本文档的读入,以及叫号。
- 创建一个空的队伍列表 teams。
- 将传入的参赛队伍列表 list 中的队伍逐个添加到 teams 列表中。
- 调用 categorizeTeams 函数,将 teams 列表按照赛事类别进行分组,得到一个映射表 categorizedTeams,其中 key 为赛事类别,value 为该类别下的参赛队伍列表。
- 调用 createFinalRooms 函数创建9个决赛室,得到一个决赛室列表 finalRooms。
- 遍历 categorizedTeams 中的每个赛事类别:
- 初始化当前时间 nowTime 为 0。
- 获取赛事类别和对应的参赛队伍列表。如果决赛室列表中还有剩余的决赛室:获取当前的决赛室对象 room。遍历赛事类别下的每个参赛队伍:
- 调用 room.callNextTeam 函数,将当前参赛队伍作为参数传入,同时传入当前时间 nowTime。
- 模拟比赛进行的时间,暂停一段时间(这里使用 Thread.sleep 方法,暂停 0.5 秒)。
- 调用 room.finishMatch 函数,表示比赛结束。更新当前时间 nowTime,增加 0.5。递增决赛室索引 roomIndex,用于下一个赛事类别的决赛室。
- 完成所有比赛后,叫号系统的执行结束。
该算法将参赛队伍根据赛事类别进行分组,并按照顺序将队伍安排到相应的决赛室进行比赛。每个决赛室在比赛期间占用一个队伍,并在比赛结束后释放。这个算法的目标是按照预定的比赛顺序和时间安排进行比赛,模拟实际的比赛场景。
categorizeTeams
从Map的一个实例来创建映射表 Map<String, List<Team>> categorizedTeams = new HashMap<>(); 将这个映射表的类型设置为HashMap,key 为赛事类别,value 为该类别下的参赛队伍列表。然后遍历team列表,用cate来判断参赛队伍属于哪个类型,然后将他们放到对应的映射表中。
public static Map<String, List<Team>> categorizeTeams(List<Team> teams) {
Map<String, List<Team>> categorizedTeams = new HashMap<>();
for (Team team : teams) {//遍历
String cate = team.getType(); // 赛事类别存在于参赛队伍的"type"字段中
String category = getCategory(cate); // 获取当前赛事type是9类赛事类别种的第几类
List<Team> categoryTeams = categorizedTeams.get(category);
if (categoryTeams == null) {
categoryTeams = new ArrayList<>();
categorizedTeams.put(category, categoryTeams);//将一个条目放入该映射表中
}
categoryTeams.add(team);
}
return categorizedTeams;
}
getCategory
此方法与赛事管理系统中的getindex方法相似,用equal方法来判断是否相等,并且返回相应的决赛室号
// 获取当前赛事type是9类赛事类别种的第几类
public static String getCategory(String cate) {
if(cate.equals("游戏设计") || cate.equals("交互媒体设计") || cate.equals("虚拟现实VR与增强现实AR")) {
return "赛事类别1";
}
else if(cate.equals("新媒体漫画") || cate.equals("动画") || cate.equals("纪录片") || cate.equals("微电影") ||
cate.equals("数字短片")){
return "赛事类别2";
}
else if(cate.equals("平面设计") || cate.equals("产品设计") || cate.equals("环境设计")) {
return "赛事类别3";
}
else if(cate.equals("信息图形设计") || cate.equals("动态信息影像(MG动画)") || cate.equals("交互信息设计") ||
cate.equals("数据可视化")){
return "赛事类别4";
}
else if(cate.equals("人工智能实践赛") || cate.equals("人工智能挑战赛")) {
return "赛事类别5";
}
else if(cate.equals("大数据实践") || cate.equals("大数据主题")){
return "赛事类别6";
}
else if(cate.equals("城市管理") || cate.equals("医药卫生") || cate.equals("运动健身") || cate.equals("数字生活") ||
cate.equals("行业应用") || cate.equals("物联网专项")){
return "赛事类别7";
}
else if(cate.equals("计算机基础与应用类课程微课") || cate.equals("中、小学数学或自然科学课程微课") ||
cate.equals("汉语言文学") || cate.equals("虚拟实验平台")){
return "赛事类别8";
}
else if(cate.equals("Web应用与开发") || cate.equals("管理信息系统") || cate.equals("移动应用开发") ||
cate.equals("移动应用开发(非游戏类)") || cate.equals("算法设计与应用") ||
cate.equals("信创软件应用与开发") || cate.equals("区块链应用与开发")){
return "赛事类别9";
}
else {
System.out.println(cate);
return "其他";
}
}
createFinalRooms
首先,创建决赛室,得到一个决赛室列表 finalRooms
List<FinalRoom> finalRooms = createFinalRooms(); // 创建9个决赛室
然后根据赛事分类,将对应的队伍放进决赛室中
public static List<FinalRoom> createFinalRooms() {
List<FinalRoom> finalRooms = new ArrayList<>();
String[] categories = {"赛事类别1", "赛事类别2", "赛事类别3", "赛事类别4", "赛事类别5", "赛事类别6", "赛事类别7", "赛事类别8",
"赛事类别9"};
for (String category : categories) {
FinalRoom room = new FinalRoom(category);
finalRooms.add(room);
}
return finalRooms;
}
}
模拟叫号
记录需要循环的次数,该次数由和每个类别中参赛队伍的数量进行比较可得,对决赛室和每个决赛室中的参赛队伍进行循环,且记录每次参赛队伍进入比赛的时间模拟比赛进行的时间,暂停一段时间(这里使用 Thread.sleep 方法,暂停 0.5 秒)。
// 进行叫号和比赛
String category[] = new String[9] ;
List<Team> categoryTeams[] = new ArrayList[9];
int tmp = 0;
int maxRound = 0;
for (Map.Entry<String, List<Team>> entry : categorizedTeams.entrySet()) {//返回一个包含了该映射表中条目的集合
category[tmp] = entry.getKey();
categoryTeams[tmp] = entry.getValue();
int teamNum = categoryTeams[tmp].size();
maxRound = Math.max(maxRound,teamNum);
tmp++;
}
double nowTime = 0;
int round = 0;
while(round<maxRound) {
System.out.println("当前时间: "+nowTime);
for(int i=0;i<9;i++) {
if(round>=categoryTeams[i].size()) {
continue;
}
String cate = category[i];
FinalRoom room = finalRooms.get(0);
for(int j=0;j<9;j++) {
if (finalRooms.get(j).getCategory().equals(cate)) {
room = finalRooms.get(j);
break;
}
}
Team team = categoryTeams[i].get(round);
room.callNextTeam(team);
}
try {
Thread.sleep(500); // 模拟比赛进行的时间(0.5秒)
} catch (InterruptedException e) {
e.printStackTrace();
}
nowTime += 0.5;
System.out.println("比赛结束,决赛室空闲");
round++;
}
}