class Link{//链表类,外部能够看见的只有这一个类
private class Node{//定义的内部节点类
private Object data;//要保存的数据
private Node next;//下一个节点引用
public Node(Object data){
this.data = data;
}
/**
* 设置新结点的保存,所有的新结点保存在最后一个结点之后
* @param newNode 新结点对象
*/
public void addNode(Node newNode){
if (this.next == null){//当前的下一个结点为null
this.next = newNode;//保存节点
}else { //向后继续保存
this.next.addNode(newNode);
}
}
/**
* 数据检索操作,判断指定数据是否存在
* 第一次调用(Link):this = Link.root
* 第二次调用(Node):this = Link.root.next
* @param data 要查询的数据
* @return 如果数据存在返回true,否则返回false
*/
public boolean containsNode(Object data){
if (data.equals(this.data)){
return true;
}else {
if (this.next != null){
return this.next.containsNode(data);
}else {
return false;
}
}
}
/**
* 根据索引取出数据,此时该索引一定存在的
* @param index 要取得数据的索引编号
* @return 返回指定索引节点包含的数据
*/
public Object getNode(int index){
//使用当前的foot内容与要查询的索引进行比较,随后将foot的内容自增,目的是下次查询方法
if (Link.this.foot++ == index){//当前为要查询的索引
return this.data;//返回当前节点数据
}else {//继续向后查询
return this.next.getNode(index);//进行下一个节点的判断
}
}
/**
* 修改指定索引节点包括数据
* @param index 要修改的索引编号
* @param data 新数据
*/
public void setNode(int index,Object data){
//使用当前的foot内容与要查询的索引进行比较,随后将foot的内容自增,目的是下次查询方便
if (Link.this.foot++ == index){//当前为要修改的内容
this.data = data;//进行内容修改
}else {
this.next.setNode(index,data);//继续下一个节点的索引判断
}
}
/**
* 节点的删除操作,匹配每一个结点的数据,如果当前结点数据符合删除数据
* 则使用"当前结点上一个结点.next = 当前节点.next"方式空出当前结点
* 第一次调用(Link),previous = Link.root、this = Link.root.next
* 第二次调用(Node),previous = Link.root、this = Link.root.next.next
* @param previous 当前节点的上一个结点
* @param data 要删除的数据
*/
public void removeNode(Node previous,Object data){
if (data.equals(this.data)){ //当前结点为要删除的结点
previous.next = this.next;//空出当前节点
}else {//继续向后删除
this.next.removeNode(this,data);//继续判断下一个
}
}
/**
* 将结点中保存的内容转化为对象数组
* 第一次调用(Link):this = Link root;
* 第一次调用 (Node):this = Link.root.next;
*/
public void toArrayNode(){
Link.this.retArray[Link.this.foot++] = this.data;//取出数据并保存在数组中
if (this.next != null){ //有后续元素
this.next.toArrayNode(); //继续下一个数据的取得
}
}
}
private Node root; //根节点定义
private int count = 0; //保存元素的个数
private int foot = 0; //结点索引
private Object[] retArray; //返回的数组
/**
* 用户向链表增加新的数据,在增加时要将数据封装为Node类,这样才可以匹配结点顺序
* @param data 要保存的数据
*/
public void add(Object data){ //假设不允许有null
if (data == null){ //判断数据是否为空
return; //结束方法调用
}
Node newNode = new Node(data);//要保存的数据
if (this.root == null){//当前没有根结点
this.root = newNode;//保存根结点
}else {//根结点存在
this.root.addNode(newNode);//交给Node类处理结点的保存
}
this.count++;//数据保存成功后保存个数加一
}
/**
* 取得链表中保存的数据个数
* @return 保存的个数,通过count属性取得
*/
public int size(){ //取得保存的数据量
return this.count;
}
/**
* 判断是否为空链表,表示长度为0,不是null
* @return 如果链表中没有保存任何数据则返回true,否则返回false
*/
public boolean isEmpty(){
return this.count == 0;
}
/**
* 数据查询操作,判断指定数据是否存在,如果链表没有数据直接返回false
* @param data 要判断的数据
* @return 数据存在返回true,否则返回false
*/
public boolean contains(Object data){
if (data == null || this.root == null){ //现在没有要查询的数据,根节点也不保存数据
return false; //没有查询结果
}
return this.root.containsNode(data); //交由Node类查询
}
/**
* 根据索引取得保存的节点数据
* @param index 索引数据
* @return 如果要取得的索引内容不存在或者大于保存个数,返回null,反之返回数据
*/
public Object get(int index){
if (index > this.count){
return null;
}
this.foot = 0;
return this.root.getNode(index);
}
/**
* 根据索引修改数据
* @param index 要修改数据的索引编号
* @param data 新的内容数据
*/
public void set(int index,Object data){
if (index > this.count){ //判断是否超过了保存范围
return; //结束方法调用
}
this.foot = 0; //重新设置foot属性的内容,作为索引出现
this.root.setNode(index,data); //交给Node类设置数据内容
}
/**
* 链表数据的删除操作,在删除前要先使用contain()判断链表中是否存在指定数据
* 如果要删除的数据存在,则首先判断根节点的数据是否为要删除数据
* 如果是,则将根节点的下一个节点作为新的根节点
* 如果要删除的数据不是根节点数据,则将删除操作交由Node类的removeNode()方法完成
* @param data 要删除的数据
*/
public void remove(Object data){
if (this.contains(data)){
if (data.equals(this.root.data)){
this.root = this.root.next;
}else {
//此时根元素已经判断过了,从第二个元素开始判断,即第二个元素的上一个元素为根节点
this.root.removeNode(this.root,data);
}
this.count--; //删除成功后个数要减少
}
}
/**
* 将链表中的数据转换为对象数组输出
* @return 如果链表没有数据,返回null,如果有数据,则将数据转变为对象数组后返回
*/
public Object[] toArray(){
if (this.root == null){ //判断链表是否有数据
return null; //没有数据,返回null
}
this.foot = 0; //脚标清零操作
this.retArray = new Object[this.count]; //根据保存内容开辟数组
this.root.toArrayNode(); //交给Node类处理
return this.retArray; //返回数组对象
}
/**
* 清空链表数据
*/
public void clear(){
this.root = null;//清空链表
this.count = 0;//元素个数为0
}
}
interface Pet{//定义一个宠物的标准
/**
* 取得宠物的名字
* @return 宠物名字信息
*/
public String getName();
/**
* 取得宠物的年龄
* @return 宠物年龄信息
*/
public int getAge();
}
class PetShop{//一个宠物商店要保存多个宠物信息
private Link pets = new Link();//保存的宠物信息
/**
* 新的宠物类型上架操作,向链表中保存宠物信息
* @param
*/
public void add(Pet pet){
this.pets.add(pet); //向链表中保存数据
}
/**
* 宠物下架操作,通过链表删除保存的信息,需要对象比较equals()方法的支持
* @param pet 要删除的宠物信息
*/
public void delete(Pet pet){
this.pets.remove(pet);//从链表中删除宠物信息
}
//模糊查询一定是返回多个内容,不知道多少个,返回link即可
/**
* 宠物数据的模糊查询,首先要取得全部保存的宠物信息
* 然后采取循环的方式依次取出每一种宠物信息,并且对名称进行判断
* @param keyWorld 模糊查询关键字
* @return 宠物信息通过Link类型返回,如果有指定查询关键字的宠物信息则通过Link集合返回,否则返回null
*/
public Link search(String keyWorld){
Link result = new Link();//保存结果
//将集合变成为对象数组的形式返回,因为集合保存的是Object
//但是真正要查询的数据在Pet接口对象的getName()方法的返回值
Object obj[] = this.pets.toArray();
for (int x = 0;x < obj.length;x++){
Pet p = (Pet) obj[x];//向下转型找到具体的宠物对象
if (p.getName().contains(keyWorld)){//查询到了
result.add(p);//保存满足条件的结果
}
}
return result;
}
}
class Cat implements Pet{
private String name;
private int age;
public Cat(String name,int age){
this.name = name;
this.age = age;
}
public boolean equals(Object obj){
if (this == obj){
return true;
}
if (obj == null){
return false;
}
if (!(obj instanceof Cat)){
return false;
}
Cat c = (Cat) obj;
if (this.name.equals(c.name) && this.age == c.age){
return true;
}
return false;
}
public String getName(){ //覆写接口中的方法
return this.name;
}
public int getAge(){ //覆写接口中的方法
return this.age;
}
public String toString(){
return "猫的名字:" + this.name + ",年龄:" + this.age;
}
}
class Dog implements Pet{
private String name;
private int age;
public Dog(String name,int age){
this.name = name;
this.age = age;
}
public boolean equals(Object obj){//判断对象及属性是否相同的方法
if (obj == null){//对象为空
return false;//返回false
}
if (this == obj){//如果对象相同
return true;
}
if (!(obj instanceof Dog)){//判断对象是否为Dog的实例
return false;
}
Dog c = (Dog) obj;//如果是Dog的实例,进行转型,如果不转型,用的还是Object类
//没有Dog类所拥有的属性,要转换为自己需要用的类来取值
if (this.name.equals(c.name) && this.age == c.age){
//判断名字属性和年龄属性是否相同
return true;
}
return false;
}
public String getName(){ //覆写接口中的方法
return this.name;
}
public int getAge(){ //覆写接口中的方法
return this.age;
}
public String toString(){
return "狗的名字:" + this.name + ",年龄:" + this.age;
}
}
public class TestDemo11 {
public static void main(String[] args) {
PetShop shop = new PetShop();//实例化宠物商店
shop.add(new Cat("波斯猫",1));
shop.add(new Cat("波米拉猫",1));
shop.add(new Cat("迪罗猫",2));
shop.add(new Dog("松狮",1));
shop.add(new Dog("波尔多",2));
shop.delete(new Cat("波米拉猫",1));
Link all = shop.search("波");
Object obj[] = all.toArray();
for (int x = 0;x < obj.length;x++){
System.out.println(obj[x]);
}
}
}
本程序主要的功能就是利用链表操作宠物信息,在增加、删除宠物信息时,接收的参数都是pet接口类型,这样只要是此接口的子类对象都可以进行链表操作。而在进行模糊信息查询时,由于满足条件的宠物名称会存在多个,所以方法返回类型为link。
接收的参数都是pet接口类型,将宠物信息向上转型了,都转成pet类型。
宠物商店的程序看了好久,因为其中蕴含的向下转型的知识没有掌握好,导致看不懂代码。
现在能大致的理解向下转型和向上转型的意思了,当我们的程序包含很多东西,我们要把这些东西放在一个统一的集合里,我们需要向上转型,以统一的标准保存这些东西,当我们需要这些东西的特有的属性时,我们需要把这些东西从集合中取出,向下转型,让他们再次得到自己的私有属性。从这个过程中也可以看出,为什么说向下转型的过程是先向上转型,再向下转型。