设计思路:
宠物商店需要一个链表(单向链表随笔可查)来进行宠物信息的存储,一个商店对应多个宠物,多数量的宠物应该具有同一标准,所以需要一个IPet接口来定义宠物标准,然后宠物类为IPet接口的实现类。
链表接口:
package Demo_1_29_宠物商店设计; // 通过接口设置标准 public interface ILink <E> { // 设置泛型避免安全隐患 public void add(E e); // 增加数据 public int size(); // 获取数据的个数 public boolean isEmpty(); // 判断集合是否为空 public Object[] toArray(); // 将集合元素以数组的形式返回保存 public E get(int index); // 根据索引获取数据 public void set(int index, E data); // 修改指定索引数据 public boolean contains(E data); // 判断数据是否存在 public void remove(E e); // 删除指定节点 public void clean(); // 清空链表 }
链表实现类:
package Demo_1_29_宠物商店设计; /* * 整个过程中LinkImpl类只是进行root节点的创建和add方法参数的接收并向Node节点发出指令addNode(),真正的节点操作类是LinkImpl类中的Node类 * 而节点的连接其实就是引用的传递,Node类只管进行将后节点的引用传给给前节点的next来记录 * data的值是通过客户端传入到LinkImpl的add()方法,所以Node节点并不关心data的值 * */ public class LinkImpl<E> implements ILink<E> { // 内部类的属性是提供给外部类使用,那么就没有必要再加上setter和getter方法了 private class Node{ //保存节点的数据关系 private E data; //保存的数据 private Node next; // 下一个节点的引用 public Node(E data){ // 有数据才有意义 this.data = data; } // 第一次调用:this = LinkImpl.root; // 第二次调用:this = LinkImpl.root.next; // 第三次调用:this = LinkImpl.root.next.next; public void addNode(Node newNode){ // 保存新的Node数据 if (this.next == null){ //当前节点(根节点)的下一个节点为空 // 下面将新节点(下一节点)的引用传给当前节点的next以记录地址 this.next = newNode; // 根节点的下一节点为newNode(将新节点与前一节点成功连接) }else { this.next.addNode(newNode); // 递归调用方法知道找出下一节点为空的节点并保存新节点 } } // 第一次调用: this = LinkImpl.root // 第二次调用: this = LinkImpl.root.next // 第三次调用: this = LinkImpl.root.next.next public void toArrayNode(){ // 将数据添加到返回数组中 LinkImpl.this.returnData [LinkImpl.this.foot ++ ] = this.data; // 当前data添加到数组中 if (this.next != null){ //还有下一个数据 this.next.toArrayNode(); // 这一步是this.next调用的toArrayNode方法,递归进行数据的添加 } } public E getNode(int index){ if (LinkImpl.this.foot ++ == index){ // 定位至指定的索引 return this.data; // 返回当前数据 }else { return this.next.getNode(index); // 递归调用继续向下匹配指定的索引 } } public void setNode(int index, E data){ if (LinkImpl.this.foot ++ == index){ // 定位至指定的索引 this.data = data; // 返回当前数据 }else { this.next.setNode(index, data);// 递归调用继续向下匹配指定的索引 } } public boolean containsNode(E data){ if (data.equals(this.data)){ // 想要查询的数据等于当前数据则表示存在 return true; // 返回真 }else { // 想要查询的数据不等当前数据则表示想要查询的数据不在当前位置 if (this.next == null){ return false; // 当前位置的下一个数据的地址为空返回false(后面没有数据了) }else { return this.next.containsNode(data); // 继续判断想要查询的数据是否在下一个位置 } } } public void removeNode(Node previous,E data){ if (this.data.equals(data)){ // 找到指定位置的节点 System.out.println("删除了节点数据为:" + data + "、地址为:" + previous.next); previous.next = this.next; // 上一节点与当前节点的下一节点连接,以空出当前节点 }else { // 当前节点不是指定节点 if (this.next != null){ // 如果当前节点的下一个节点不为空 this.next.removeNode(this,data); // 这里传入到下一个节点,this是当前节点(下一节点的上节点),data是指定的节点 } } } } /*--------------- 以下为LinkImpl类中定义的成员 ------------------*/ private Node root; // 保存根元素 private int count; // 保存数据的个数 private int foot = 0; // 操作数组的脚标 private Object[] returnData; //返回的数据 /*--------------- 以下为LinkImpl类中定义的方法 ------------------*/ @Override public void add(E e) { if (e == null) {//保存的数据为空 return; } // 数据本身不具备关联特性,只有Node类有,那么想实现关联处理,就必须将数据包装在Node类中 Node newNode = new Node(e); // 创建一个新的节点,传入的数据e通过Node类的构造方法赋值给data if (this.root == null){ this.root = newNode; // 第一个节点为根节点 }else{ // 根节点存在 this.root.addNode(newNode); } this.count ++; } @Override public int size() { return this.count; } @Override public boolean isEmpty() { // return this.root == null; return this.count == 0; // 两个return效果都是一样的,为空则返回true,否则返回false } @Override public Object[] toArray() { if (this.isEmpty()){ // 空集合 return null; } this.foot = 0; // 脚标起始为0 this.returnData = new Object[this.count]; // 当前数据的个数就是返回数组的长度 this.root.toArrayNode(); // 利用Node类进行递归获取数据 return this.returnData; } @Override public E get(int index) { if (index >= this.count){ // 索引大于数据的个数 return null; } //索引数据的获取应该由Node类完成 this.foot = 0; return this.root.getNode(index); } @Override public void set(int index, E data) { if (index >= this.count) { // 索引大于数据的个数 return; //结束 } //索引数据的修改应该由Node类完成 this.foot = 0; // 重置索引下标 this.root.setNode(index,data); // 修改数据 } @Override public boolean contains(E data) { if (data == null){ return false; // 想查看的数据为空返回false } return this.root.containsNode(data); } @Override public void remove(E data) { if (this.contains(data)){ // 判断想要删除的节点是否存在 if (this.root.data.equals(data)){ // 如果想要删除的节点已存在,判断是否为根节点 System.out.println(data + "-->数据为根节点已经删除!"); this.root = this.root.next; // 删除根节点 // 根节点判断完成,以后应该从下一节点开始判断 }else { this.root.next.removeNode(this.root,data); // 不是根节点那么就让Node节点进行操作,传入根节点作为节点的上一节点,和指定节点data } this.count --; } } @Override public void clean() { this.root = null; // 根节点重置为空(根节点后指向的地址重置为空),后续所有节点为空 this.count = 0; // 个数清零 } }
宠物商店类:
package Demo_1_29_宠物商店设计; public class PetShop { //宠物商店 private ILink<IPet> allPets = new LinkImpl<>(); public void add(IPet pet){ // 宠物上架 this.allPets.add(pet); // 集合中保存对象 } public void delete(IPet pet){ this.allPets.remove(pet); } public ILink<IPet> search(String keyword){ ILink<IPet> searchRes = new LinkImpl<IPet>(); // 创建查询链表保存查询结果 Object[] res = this.allPets.toArray(); // 获取全部数据 if (res != null){ for (Object obj: res){ IPet pet = (IPet) obj; if (pet.getName().contains(keyword) || pet.getColor().contains(keyword)){ // contains方法字符匹配,输入的kryword在名字中或颜色中时返回true searchRes.add(pet); // 保存查询结果 } } } return searchRes; } }
宠物接口:
package Demo_1_29_宠物商店设计; public interface IPet { // 宠物接口,定义宠物标准 public String getName(); // 获取宠物的名字 public String getColor(); // 获取宠物的颜色 }
宠物类(接口实现类)
package Demo_1_29_宠物商店设计; public class Cat implements IPet{ // 实现宠物标准 private String name; private String Color; public Cat() { } public Cat(String name, String color) { this.name = name; this.Color = color; } @Override public String getName() { return this.name; } public void setName(String name) { this.name = name; } @Override public String getColor() { return this.Color; } public void setColor(String color) { this.Color = color; } public boolean equals(Object o){ if (o == null){ return false; } if (!(o instanceof Dog)){ // 不是猫 return false; } if (this == o){ return true; } Cat cat = (Cat) o; return this.name.equals(cat.name) && this.Color.equals(cat.Color); } public String toString(){ return "【宠物猫】" + "|-【名字】:" + this.name + ", 【颜色】:" + this.Color + "。"; } }
package Demo_1_29_宠物商店设计; public class Dog implements IPet{ // 实现宠物标准 private String name; private String Color; public Dog() { } public Dog(String name, String color) { this.name = name; this.Color = color; } @Override public String getName() { return this.name; } public void setName(String name) { this.name = name; } @Override public String getColor() { return this.Color; } public void setColor(String color) { this.Color = color; } public boolean equals(Object o){ if (o == null){ return false; } if (!(o instanceof Dog)){ // 不是猫 return false; } if (this == o){ return true; } Dog dog = (Dog) o; return this.name.equals(dog.name) && this.Color.equals(dog.Color); } public String toString(){ return "【宠物狗】:" + "|-【名字】:" + this.name + ", 【颜色】:" + this.Color + "。"; } }
操作主类(客户端):
package Demo_1_29_宠物商店设计; import java.util.Arrays; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); PetShop shop = new PetShop(); shop.add(new Dog("斑点狗", "黑白色")); shop.add(new Dog("小黑狗", "黑色")); shop.add(new Cat("加菲猫", "橙黄色")); shop.add(new Cat("狸花猫", "狸花色")); System.out.printf("请输入查询宠物信息的关键字:"); String keyWord = sc.next(); Object[] res = shop.search(keyWord).toArray(); // System.out.println(Arrays.toString(res)); for (Object o : res){ System.out.println(o); } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)