宠物商店这个题目来自阿里云大学Java基础的课程中,题目内容不是重要,这里主要是看如何实现单向链表的。
在实现链表前先思考一下几个问题:
- java中没有指针,怎么实现链表?
- 链表并不知道用户会存放什么样数据类型的数据,怎么办?
- 如果需要用到内部类,有哪些需要注意的?
- 如果遇到转型问题,有哪些需要注意的?
- 代码设计还是要遵循七大原则的,你怎么理解的?
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();//集合清空
}
class LinkImpl<E> implements ILink<E>{
private class Node{//Node保存节点的数据关系,而LinkImpl操作根节点
private E data; //保存数据
private Node next; //引用关系
public Node(E data){
this.data=data;
}
public void addNode(Node newNode){
if(this.next==null){
this.next=newNode;
}else{
this.next.addNode(newNode);//递归
}
}
public void toArrayNode(){
LinkImpl.this.returnData[LinkImpl.this.foot++]=this.data;
if(this.next!=null){
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(this.data.equals(data)){
return true;
}else{
if(this.next==null){
return false;
}else{
return this.next.containsNode(data);
}
}
}
public void removeNode(Node previous,E data){
if(this.data.equals(data)){
previous.data=this.next.data;
}else{
if(this.next!=null){
this.next.removeNode(this, data);
}
}
}
}
//------------以上为内部类------------------
private Node root;//保存根节点
private int count;//保存数据个数
private int foot;//操作数组的脚标
private Object [] returnData;//保存返回的数组
public void add(E e){
if(e==null){
return ;
}
Node newNode=new Node(e);
if(this.root==null){
this.root=newNode;
}else{
this.root.addNode(newNode);//将新节点保存在合适的位置
}
this.count++;
}
public int size(){
return this.count;
}
public boolean isEmpty(){
//return this.root==null;
return this.count==0;
}
public Object [] toArray(){
if(this.isEmpty()){
return null;
}
this.foot=0;
this.returnData=new Object[this.count];
this.root.toArrayNode();
return this.returnData;
}
public E get(int index){
if(index>=this.count){
return null;
}
this.foot=0;
return this.root.getNode(index);
}
public void set(int index,E data){
if(index>=this.count){
return ;
}
this.foot=0;
this.root.setNode(index,data);
}
public boolean contains(E data){
if(data==null){
return false;
}
return this.root.containsNode(data);//交给Node类判断
}
public void remove(E data){
if(this.contains(data)){
if(this.root.data.equals(data)){
this.root=this.root.next;
}else{
this.root.next.removeNode(this.root, data);
}
}
this.count--;
}
public void clean(){
this.root=null;
this.count=0;
}
}
interface Pet{ //宠物标准
public String getName();
public String getColor();
}
class pShop{ //宠物商店
private ILink<Pet> allPet=new LinkImpl<Pet>();//保存多个宠物
public void addPet(Pet pet){
this.allPet.add(pet);
}
public void deletePet(Pet pet){
this.allPet.remove(pet);
}
public ILink<Pet> search(String keyword){
ILink<Pet> resultsearch=new LinkImpl<Pet>();
Object result[]=this.allPet.toArray();//获取全部数据
if(result!=null){
for(Object temp:result){
Pet pet=(Pet) temp;//必须向下转型
if(pet.getName().contains(keyword)||pet.getColor().contains(keyword)){
resultsearch.add(pet);
}
}
}
return resultsearch;
}
}
class Cats implements Pet{
public String neme;
public String color;
public Cats(){}
public Cats(String name,String color){
this.color=color;
this.neme=name;
}
@Override
public String getName(){
return this.neme;
}
@Override
public String getColor(){
return this.color;
}
public boolean equals(Object obj){
if(obj==null){
return false;
}
if(!(obj instanceof Cats)){
return false;
}
if(this==obj){
return true;
}
Cats cat = (Cats) obj;
return this.neme.equals(cat.neme)&&this.color.equals(cat.color);
}
public String toString(){
return "宠物猫.名字: "+this.neme+", 颜色: "+this.color;
}
}
class Dogs implements Pet{
public String neme;
public String color;
public Dogs(){}
public Dogs(String name,String color){
this.color=color;
this.neme=name;
}
@Override
public String getName(){
return this.neme;
}
@Override
public String getColor(){
return this.color;
}
public boolean equals(Object obj){
if(obj==null){
return false;
}
if(!(obj instanceof Dogs)){
return false;
}
if(this==obj){
return true;
}
Dogs dog = (Dogs) obj;
return this.neme.equals(dog.neme)&&this.color.equals(dog.color);
}
public String toString(){
return "宠物狗.名字: "+this.neme+", 颜色: "+this.color;
}
}
public class petShop {
public static void main(String agrs[]){
pShop shop=new pShop();
shop.addPet(new Cats("小猫","白色"));
shop.addPet(new Cats("大猫","黑色"));
shop.addPet(new Dogs("小狗","黑色"));
shop.addPet(new Dogs("大狗","黄色"));
shop.deletePet(new Cats("小猫","白色"));
Object result[]= shop.search("小").toArray();
for(Object obj:result){
System.out.println(obj);
}
}
}
下面回答一下上面的六个问题:
-
java中没有指针,怎么实现链表?
Java中没有指针,却在很多地方运用了指针的概念——对象引用。那么当前节点如何指向后一个节点呢?在C语言中,单向链表的节点有数据域和指针域,数据域存放数据,指针域指向下一个节点的地址。其实在Java中也类似,节点之中有数据域,但还需要加上引用关系,就是添加一个节点类,表面看上去像一个节点包含着一个节点。 -
链表并不知道用户会存放什么样数据类型的数据,怎么办?
有两种方法,第一个是使用Object类,第二种就是Java中的泛型了。好像两者使用起来没有太大的区别,那就谈谈泛型和Object的区别:如果使用Object类,Object类是所有类的父类,使用的时候要用到强制类型转换(这个问题到第4问还会说)转换到其他类,但如果在强制类型转换时的类型和方法返回类型不一致(比方说,方法返回的是一个String,却不注意转型成Integer),就会发生类型转换异常。使用泛型就不存在类型转换和异常了,代码鲁棒性更高。 -
如果需要用到内部类,有哪些需要注意的?
内部类对象可以访问创建它的外部类对象的内容,外部类对象如果要访问内部类对象,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问。内部类如果不是static的,那么它可以访问创建它的外部类对象的所有属性内部类,如果是static的,那么它只可以访问创建它的外部类对象的所有static属性。当从外部类继承的时候,内部类是不会被覆盖的,它们是完全独立的实体,每个都在自己的命名空间内,如果从内部类中明确地继承,就可以覆盖原来内部类的方法。 -
如果遇到转型问题,有哪些需要注意的?
方法的多态性:方法的重载,方法的覆写。
对象的多态性:父子实例之间的转换处理,两种模式:对象向上转型:父类 父类实例=new 子类实例、自动完成转换;(右侧创建一个子类对象,把它当做父类来看,向上转型一定是安全的,从小范围转换成为大范围)。对象向下转型:子类 子类实例=new (子类)父类实例、强制完成转换。(这样从一般到特殊就不一定安全了,比方说子类除了继承父类外还进行了扩展,而这些是父类没有的,如果需要使用这些扩展的功能就会失败),所以可以使用instanceof运算符来在运行时判断对象是否是指定类及其父类的一个实例。
注意,为了安全,在向下转型之前先要进行向上转型。打个比方,有超人,人,怪兽三个类,超人也属于人是人的子类,人是超人的父类。超人平时都是以普通人的身份生活的,当遇到怪兽时就变身打怪兽,而普通人装逼当做超人打怪兽,可能会被怪兽打死吧。 -
代码设计还是要遵循七大原则的,你怎么理解的?
这篇文章有专门对七大原则讲解:OOP的七大原则