实验目的:
- 进一步掌握Object类的主要特点;
- 进一步掌握上转型、下转型的含义;
- 掌握equals方法的具体应用;
- 掌握开发一个链表类的关键步骤。
- 利用链表类实现复杂程序。
- 掌握接口的概念和语法,并能够应用于实际。
● 参考学时:2学时
● 基本要求:
- 定义实验61.Node类和实验61.LinkedList类,并通过测试类测试上述类的正确性。LinkedList类组合了Node类,Node类与LinkList类之间的关系如图6.1所示。图中空心菱形的含义是LinkedList类聚合(Aggregation)了Node类对象,聚合是组合关系的一种。
- 注意,Java语言已经提供了一个java.util.LinkedList类,请不要在本实验中导入并使用该类,而是重新定义“实验61.LinkedList”。
图6-1 Node类和LinkedList类之间的关系
- Node类:
- 要求Node类拥有如下2个私有属性:
- data:用于存放节点的数据。为了能够实现常见数据类型的链表,data类型定义为Object,即可以在链表中保存任何类型的对象。如果要保存int、double等基本数据类型,需要用到Java的自动“装箱”和“拆箱”机制。
- next:用于保存本节点的下一个节点的引用。
- 要求Node类里有一个构造方法Node(Object data),此外还拥有设置属性和获取属性的getter、setter方法以及简单的toString()方法(该方法将data转换为String并返回该值)。
- LinkedList类
- 要求LinkedList类拥有1个private属性head,用于存储链表第一个节点的引用。
- 要求为LinkedList实现7个public方法,关于这7个方法的要求如表6.1所示。
- 实现search()方法和remove()方法时需要用到链表中节点的数据所属类提供的equals()方法,因为这两个方法都涉及到比较链表中某一Node的getData().equals(所要查找的data),而不是getData()= = data。
表6.1 LinkList类的方法
编号 | 方法名称 | 描述 |
1 | void add(Object data) | 将值为data的节点插入到链表尾。 |
2 | boolean remove(Object data) | 在链表中删除值为data的节点。若删除成功,返回true;否则,返回false。 |
3 | Node search(Object data) | 在链表中查找值为data的节点。若找到,返回该节点的引用,若没找到,返回null。 |
4 | boolean insertAfter(Node previous, Object data) | 在引用previous指向的节点后插入一个值为data的节点。若插入成功,返回true;否则,返回false。 |
5 | Node get(int index) | 在链表中找第index个节点。若找到,返回第index节点的引用,若找不到,返回null。 |
6 | boolean set(Node node, Object data) | 将node引用节点的内容改为data。若修改成功,返回true;否则,返回false。 |
7 | int size() | 返回链表长度。 |
一个初步的具有add方法的LinkedList代码如下:
public class LinkedList {
private Node head;
public void add(Object data) {
Node node=new Node(data); //创建包含data值的Node节点
if(head==null) //链表为空,则当前节点就是第一个节点
head=node;
else { //链表不为空,需要找到最后一个节点
Node tmp=head; //利用tmp节点遍历
while(tmp.getNext()!=null) //tmp节点不是最后一个节点
tmp=tmp.getNext(); //tmp指向下一个节点
//循环结束时tmp为最后一个节点
tmp.setNext(node); //把当前节点链接在最后一个节点之后
}
}
}
- 实验61.Test类(测试类,其中含主方法)
- 要求在测试类的主方法中,测试LinkedList类的方法,可按如下步骤执行:
① 调用无参构造方法LinkedList(),创建一个空链表。
② 连续三次调用add()方法,在链表中插入三个Integer对象,如add(1); add(2); add(3)(此处Java自动将1、2、3等int值装箱为Integer对象)。
③ 调用search()方法,查找值为2的节点,将该方法返回的节点的引用赋给Node型变量p;
④ 调用set()方法,将p指向的节点的内容修改为22。
⑤ 调用insertAfter()方法,在p指向的节点后面插入一个值为23的节点。
⑥ 调用remove()方法,删除值为22的节点。
⑦ 调用size()方法,获得链表长度赋给整型变量n。
⑧ 循环n次,每一轮通过调用get(i)方法得到第i个节点的引用,并将其内容输出。
- 在“实验61.LinkedList”和“实验61.Node”基础上,实现宠物商店的宠物上架、下架、查询等操作,其中宠物商店中的宠物需要用链表存储。在本程序中,最重要的是定义宠物标准(定义一个宠物接口Pet),进入宠物商店销售的所有宠物(如Cat类、Dog类、Fish类)都需要实现这个标准(即实现接口Pet)。根据以上原则,给出了图6.2所示的类图(图中的虚线三角形箭头表示实现接口,实线普通箭头表示关联(Association)关系)。
图6.2 实验相关的类、接口及其关系
- 定义宠物接口实验62.Pet
这个接口是宠物商店可处理的宠物标准。宠物商店并不关心具体的宠物是什么,只关心一点:只要是实现了标准接口的宠物,就可以进入宠物商店,并能够通过宠物商店进行各种操作。宠物接口Pet的定义如下:
package 实验62;
interface Pet {
public String getName();
public int getAge();
}
- 定义宠物商店类实验62.PetShop
宠物商店所售卖的宠物数量不定,因此要用链表(LinkedList)对象保存多个宠物。同时宠物商店能够售卖的宠物必须是具有宠物特征(比如具有名字和年龄)和行为(比如告知名字或年龄)的一切动物,因此在上架(添加)、卖出(删除)宠物时,接收的参数都应是Pet接口类型。
表6.2 宠物商店类的方法
编号 | 方法名称 | 描述 |
1 | void add(Pet pet) | 将宠物pet上架,即添加到链表中。 |
2 | boolean delete(Pet pet) | 从宠物商店下架宠物pet,即从链表中查找并删除。若删除成功,返回true;否则,返回false。 |
3 | Pet get(int index) | 查找第index个宠物。若找到,返回该宠物的引用,若找不到,返回null。 |
4 | int size() | 返回宠物总数。 |
提示:在宠物商店类中,先定义一个LinkedList类变量pets,并实例化。
package 实验62;
import 实验61.*;
public class PetShop { // 一个宠物商店要保存多个宠物信息
private LinkedList pets = new LinkedList(); //用链表保存宠物信息
public void add(Pet pet) { //新宠物上架操作,以接口对象为参数!
pets.add(pet);
}
public boolean delete(Pet pet) { //宠物下架操作,以接口对象为参数!
…
}
public Pet get(int index){
//pets.get()返回Node节点,Node节点的getData()返回Object,该Object就是Pet
return (Pet)( pets.get(index).getData() );
}
…
}
- 定义宠物猫子类实验62.Cat
要求Cat类实现接口Pet。要求Cat类有两个私有属性name和age。Cat类中除了getName()和get getAge( ) 方法外,还要求为其实现3个public方法,关于这3个方法的要求如表6.4所示。Cat类的类图如图6.3所示。为了简洁起见,在图6.3所示的Cat类图中省略了getName()方法和getAge( ) 方法。
表6.3 Cat类的方法表
编号 | 方法名称 | 描述 |
1 | Cat(String name, int age) | 创建Cat类的实例,其名字为name,年龄为age。 |
2 | equals(Object obj) | 覆写Object中的equals()方法。判断猫类的两个对象值是否相等。 |
3 | toString() | 覆写Object中的方法。返回猫的完整信息。 |
- 定义宠物狗子类实验62.Dog
要求Dog类实现接口Pet。要求Dog类有两个私有属性name和age。Dog类中除了getName()和get getAge( ) 方法外,还要求为其要求实现3个public方法,对这3个方法的要求,与表6.4中对Cat类中方法的要求类似,因此不再赘述。Dog类的类图如图6.4所示。
图6.3 Cat类图 图6.4 Dog类图
- 编写测试类实验62.Test。要求在测试类的主方法中,完成如下动作:
① 定义一个宠物商店类变量shop,并实例化;
② 在宠物商店shop中添加1岁的“波斯猫”;
③ 在宠物商店shop中添加2岁的“橘猫”;
④ 在宠物商店shop中添加1岁的“折耳猫”;
⑤ 在宠物商店shop中添加1岁的“柯基犬”;
⑥ 在宠物商店shop中添加2岁的“波尔多”(注:波尔多是一种狗名字);
⑦ 显示宠物商店shop中所有宠物;
⑧ 删除宠物商店shop中2岁的“橘猫”;
⑨ 显示宠物商店shop中所有宠物;
通过运行结果可以发现,由于本程序是面向接口的编程,所以返回的结果中即包含了Cat类对象,也包含了Dog类对象。
表6.4 链表类测试案例(仅用于编码测试)
编号 | 测试目的 | 输入或测试数据 | 期望结果 |
1 | 构造方法 | list=new LinkedList() | list.size()返回0; list.get(0)返回null; list.search(1)返回null; list.remove(1)返回false |
2 | add方法 | list.add(1); list.add(2); list.add(3); | list.size()返回3 |
3 | search方法 | Node p= list.search(22); | System.out.println(p)显示null |
4 | search方法 | Node p= list.search(2); | System.out.println(p)显示2 |
5 | set方法 | list.set(null,"22"); | set方法返回false |
6 | set方法 | list.set(p,"22"); | System.out.println(p)显示22 |
7 | insertAfter方法 | list.insertAfter(p,23); | list.size()返回4 |
8 | insertAfter方法 | list.insertAfter(null,23); | insertAfter方法返回null |
9 | remove方法 | list.remove(12) | 返回false |
10 | remove方法 | list.remove(22) | 返回true |
11 | get方法 | Node p=list.get(0); | System.out.println(p)显示1 |
12 | get方法 | Node p= list.get(-1); | System.out.println(p)显示null |
13 | get方法 | Node p= list.get(100); | System.out.println(p)显示null |
表6.5 宠物商店测试案例(仅用于编码测试)
编号 | 测试目的 | 输入或测试数据 | 期望结果 |
1 | 构造方法 size方法 get方法 delete方法 | shop=new PetShop() | shop.size()返回0; shop.get(0)返回null; shop.delete(null)返回false |
2 | add方法 get方法 toString方法 | shop.add(new Cat("波斯猫",1)); shop.add(new Cat("橘猫",2)); shop.add(new Cat("折耳猫",1)); shop.add(new Dog("柯基犬",1)); shop.add(new Dog("波尔多",2)); | shop.size()返回5 shop.get(0).toString()返回“波斯猫,1岁” |
3 | delete方法 | shop.delete(new Cat("橘猫",1)); | 返回false,因为商店中只有2岁橘猫,没有1岁橘猫 shop.size()返回5 |
4 | delete方法 | shop.delete(new Cat("波斯猫",1)); | 返回true shop.size()返回4,因为已经删除了一个波斯猫 |
代码部分
LinkedList类
package shiyan6;
public class LinkedList {
private Node head;
private int size;
public LinkedList() {
this.head = null;
this.size = 0;
}
public LinkedList(Node head) {
this.head = head;
}
public void add(Object data) {
Node node = new Node(data);
if (this.head == null) {
this.head = node;
} else {
Node temp = this.head;
if (temp.getNext() != null) {
temp = temp.getNext();
}
temp.setNext(node);
++this.size;
}
}
public boolean remove(Object data) {
Node temp = this.head;
Node m = this.head;
if (this.head == null) {
return false;
} else if (temp.getData().equals(data)) {
this.head = temp.getNext();
--this.size;
return true;
} else {
while (!temp.getData().equals(data) && temp.getNext() != null) {
m = temp;
temp = temp.getNext();
}
if (!temp.getData().equals(data)) {
return false;
} else {
m.setNext(temp.getNext());
--this.size;
return true;
}
}
}
public Node search(Object date) {
Node temp = this.head;
Node result;
while (true) {
if (temp.getData().equals(date)) {
result = temp;
break;
}
temp = temp.getNext();
}
return result;
}
public boolean inserAfter(Node previous, Object date) {
Node temp = this.head;
Node node = new Node(date);
while (temp.getNext() != null) {
if (temp.equals(previous)) {
node.setNext(temp.getNext());
temp.setNext(node);
++this.size;
return true;
}
temp = temp.getNext();
}
return false;
}
public Node getNode(int index) {
Node temp = this.head;
if (this.size >= index && index >= 0 && this.head != null) {
for (int i = 0; i < index; ++i) {
temp = temp.getNext();
}
return temp;
} else {
return null;
}
}
public boolean setNode(Node node, Object date) {
Node temp = this.head;
if (!temp.equals(node) && temp.getNext() != null) {
temp = temp.getNext();
}
if (temp.equals(node)) {
temp.setData(date);
return true;
} else {
return false;
}
}
public int size() {
return this.head == null ? 0 : this.size + 1;
}
public void show() {
for (Node temp = this.head; temp != null; temp = temp.getNext()) {
System.out.printf(temp.toString() + "\t ");
}
System.out.println();
}
}
Node类
package shiyan6;
public class Node {
private Object data;
private Node next;
public Node() {
this.setData(0);
this.setNext((Node) null);
}
public Node(Object date) {
this.setData(date);
this.setNext((Node) null);
}
public void setData(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
public void setNext(Node next) {
this.next = next;
}
public Node getNext() {
return next;
}
@Override
public String toString() {
return "Node:" + this.getData();
}
}
TestNode类
package shiyan6;
public class TestNode {
public TestNode() {
}
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add(1);
list.add(2);
list.add(3);
System.out.println("链表大小:" + list.size());
System.out.println("--------------------------");
list.show();
Node p = list.search(2);
list.setNode(p, 22);
list.inserAfter(p, 23);
System.out.println("--------------------------");
list.show();
System.out.println("链表大小:" + list.size());
System.out.println("--------------------------");
System.out.println(list.remove(1));
System.out.println(list.remove(23));
list.show();
}
}
Pet接口
package shiyan6;
public interface Pet {
String getName();
int getAge();
}
PetShop类
package shiyan6;
public class PetShop {
private LinkedList pets = new LinkedList();
public PetShop() {
}
public void add(Pet pet) {
this.pets.add(pet);
}
public boolean delete(Pet pet) {
boolean result = this.pets.remove(pet);
return result;
}
public Pet get(int index) {
Pet p = null;
if (this.pets.getNode(index) == null) {
return null;
} else {
p = (Pet) ((Pet) this.pets.getNode(index).getData());
return p;
}
}
public int size() {
return this.pets.size();
}
}
Cat类
package shiyan6;
public class Cat implements Pet {
private String name;
private int age;
public Cat() {
this("无名的猫", 0);
}
public Cat(String name, int age) {
this.age = age;
this.name = name;
}
public int getAge() {
return this.age;
}
public String getName() {
return this.name;
}
public boolean equals(Object o) {
if (!(o instanceof Cat)) {
return false;
} else {
Cat c = (Cat) o;
return this.getAge() == c.getAge() && this.getName() == c.getName();
}
}
public String toString() {
return "Cat:{ " + this.getName() + "," + this.getAge() + "岁 }";
}
}
Dog类
package shiyan6;
public class Dog implements Pet {
private String name;
private int age;
public Dog() {
this("无名的狗", 1);
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return this.age;
}
public String getName() {
return this.name;
}
public boolean equals(Object o) {
if (!(o instanceof Dog)) {
return false;
} else {
Dog c = (Dog) o;
return this.getAge() == c.getAge() && this.getName() == c.getName();
}
}
public String toString() {
return "Dog: { " + this.getName() + "," + this.getAge() + "岁 }";
}
}