Social Network即为社交网络
任务描述
该任务需要模拟一个简单的人际关系网,抽象来看就是一个无向图。具体任务有:添加点、添加边、求最短路径。健壮性主要围绕名字这部分来编写代码,即每个点对应一个唯一的名字,检查是否输入重名或不输入名字,以及检查是否重复添加边。
其实就是一个无向图,这里用了弗洛伊德算法。
健壮性
在这里有一些健壮性判断的条件:
1.初始化Person时未输入姓名,则给予一个缺省值“NONE”
2.重复加入两次点
3.在未加入点时加入边
4.重复加入两次边
5.在未加入点时计算两点距离
private boolean Robustness(int left_index, int right_index, Person left, Person right) { //健壮性判断
if (left_index == -1) {
System.out.println("The name " + left.getName() + " you entered does not exist in the graph");
return true; //若找不到left这个人
}
if (right_index == -1) {
System.out.println("The name " + right.getName() + " you entered does not exist in the graph");
return true; //若找不到right这个人
}
return left_index == right_index; //若输入的两人是同一人则返回true
}
设计/实现FriendshipGraph类
该类需要建立一个图结构所需的关系矩阵、距离矩阵,以及一个名字构成的列表,设计各种方法:添加点、添加边、求最短路径。
各开辟一个100×100的空间作为矩阵来存储,其中vertex数组用来存放关系(即两人之间是否有连线),distance数组用来存放两人之间的距离,Persons数组用来存放名字,number用来记录人数。
private final boolean[][] vertex = new boolean[100][100];
private final int[][] distance = new int[100][100];
private int number = 0; //记录人数
private final String[] persons = new String[100];
实现增加点的函数,其实现原理是:
每当调用一次addVertex函数时,先在人名列表中搜索此人名,若已存在则说明该点重复添加,抛出异常。
否则在人名列表中添加此人名,并将人数加1。
public boolean addVertex(Person person) {
for (String p : persons)
if (Objects.equals(person.getName(), p))
throw new RuntimeException("Each person has a unique name "); //存在相同名字则抛出异常
if (Objects.equals(person.getName(), "NONE")) {
System.out.println("The name you entered is not a proper name");
return false;
}
persons[number++] = person.getName();
return true;
}
实现增加边的函数,其实现原理是:
将参数left和 right的合法性进行检查,优先检查这两点是否已在图中存在,否则返回false。其次,若这条边已添加过,也输出"This edge already exists"的错误信息。
若均符合健壮性要求,则更新关系矩阵的值为true,距离矩阵的值为1。
public boolean addEdge(Person left, Person right) {//在关系图中加入边
int left_index = find_index(left);//找到left对应的下标
int right_index = find_index(right);//找到right对应的下标
if (Robustness(left_index, right_index, left, right)) return false; //健壮性判断
if (vertex[left_index][right_index]) {
System.out.println("This edge already exists");
return false; //这条边已存在
}
vertex[left_index][right_index] = true;
distance[left_index][right_index] = 1;
return true;
}
计算两点间最短路径的函数,其实现原理是:
调用判断健壮性的函数检查这两点是否已在图中存在。接下来调用Floyd算法:
private void Floyd() {
for (int i = 0; i < number; i++) {
for (int j = 0; j < number; j++) {
for (int k = 0; k < number; k++) {
if (distance[i][k] + distance[k][j] < distance[i][j]) {
distance[i][j] = distance[i][k] + distance[k][j];
}
}
}
}
}
public int getDistance(Person left, Person right) {
int left_index = find_index(left);
int right_index = find_index(right);
if (Robustness(left_index, right_index, left, right)) return 0; //健壮性判断
Floyd(); //弗洛伊德算法
int dis = distance[left_index][right_index];
return dis < 100 ? dis : -1;
}
3.3.2 设计/实现Person类
为Person类设计了get和set方法。
其中get方法可以用来获取数据,set方法用来修改数据。
健壮性检查:若提供的参数为空,则给予一个缺失值“NONE”。
public class Person {
private String name;
/*
* 无参数的构造函数
* 若无参数,则缺省值为“NONE”
*/
public Person() {
this.name = "NONE";
}
public Person(String name) {
if (name.length() == 0)
this.name = "NONE";
else this.name = name;
}
//方法getname,获取当前对象的name值
public String getName() {
return this.name;
}
//方法setname,设置当前对象的name值
public boolean setName(String name) {
if (name.length() == 0){
System.out.println("You didn't enter any characters!");
return false;
}
this.name = name;
return true;
}
}
Junit测试
第一部分:测试person类
即测试getName和setName方法,着重测试输入参数为空的情况。
@Test
public void Person_Test() {
Person rachel = new Person("Rachel");
Person ross = new Person("Ross");
Person ben = new Person("Ben");
Person kramer = new Person("Kramer");
Person NONE = new Person();
//控制台输出了"You didn't enter any characters!"的信息
//并给予NONE一个缺省值"NONE"
//--------------以下是测试getname方法--------------
assertEquals("Rachel", rachel.getName());
assertEquals("Ross", ross.getName());
assertEquals("Ben", ben.getName());
assertEquals("Kramer", kramer.getName());
//--------------以下是测试setname方法--------------
assertFalse(ross.setName(""));
assertTrue(ben.setName("TestName"));
assertEquals("TestName", ben.getName());
}
第二部分:测试friendshipGraph类
测试了三个方法:addVertex、addEdge、getDistance。
包括基本功能测试、重复输入相同对象姓名的情况、人名未输入便加入图中的情况、未被加入图中就尝试加边的情况、重复加边的情况、getDistance时但图中不存在该节点的情况。
①基本功能测试,即为与main()函数相似的常规测试,检查是否正确加入点和边,是否返回正确的最短距离。
@Test
public void FriendshipGraph_Test() {
FriendshipGraph graph = new FriendshipGraph();
Person rachel = new Person("Rachel");
Person ross = new Person("Ross");
Person ben = new Person("Ben");
Person kramer = new Person("Kramer");
graph.addVertex(rachel);
graph.addVertex(ross);
graph.addVertex(ben);
graph.addVertex(kramer);
graph.addEdge(rachel, ross);
graph.addEdge(ross, rachel);
graph.addEdge(ross, ben);
graph.addEdge(ben, ross);
assertEquals(1, graph.getDistance(rachel, ross)); //Should output 1
assertEquals(2, graph.getDistance(rachel, ben)); //Should output 2
assertEquals(0, graph.getDistance(rachel, rachel)); //Should output 0
assertEquals(-1, graph.getDistance(rachel, kramer)); //Should output -1
}
②重名元素异常测试,再次加入相同人名时是否正确抛出异常(RuntimeException)
@Test(expected = RuntimeException.class)
public void Person_Same_Name_Test() {
FriendshipGraph graph = new FriendshipGraph();
Person ben1 = new Person("Ben");
Person ben2 = new Person("Ben");
assertTrue(graph.addVertex(ben1));
graph.addVertex(ben2);
}
③检查在getDistance时输入两个相同对象姓名的情况
@Test
public void addVertex_Same_Name_Test() {
FriendshipGraph graph = new FriendshipGraph();
Person ben = new Person("Ben");
graph.addVertex(ben);
assertEquals(0, (graph.getDistance(ben, ben)));//Should output 0
}
④其他各种情况的测试
太多了,就不一一列举了。
至此,P3就正式完成了
学习Java感受
这门语言给我最大的感觉就是很博大,有种深不见底的感觉。在我学习了第一门语言C后,我又接触了诸如C++,Python,JavaScript这些语言感觉并不复杂,直到我遇见了Java,在深入学习的过程中许多崭新的概念一点一点地冲击着我的心灵——公有,私有,包,类,接口,方法,实例化,静态,动态,代码块,继承,接口,抽象类,重载,封装,多态,继承……这些都是我闻所未闻的,就好比进入了一扇新世界的大门。概念太多导致我经常将它们混淆而难以区分。感觉Java给我们提供了各种各样的用法,当然也让我有点选择困难症了,比如私有和公有的区别、匿名类和实例化的区别……让我有时候因为不懂它们的区别而难以选择合适的功能。而且Java的编译机制也让我觉得很独特,它会把一个项目文件夹下的所有.java文件全部编译为.class文件——而在C/C++中一个项目仅允许有一个.c/.cpp文件且只会产生一个exe文件。
其实对于Java的初学者来说可以将它的一些新概念类比为C/C++中类似的概念——例如类可看作是对结构体功能的拓展,扩展了方法、静态变量等;包可以类比头文件,虽然包中还有子包的概念……