Java面向对象04
来源:互联网 发布:听app数据 编辑:程序博客网 时间:2024/06/18 09:46
3.1、链表(理解)
链表是一种最基本的数据结构,但是对于数据结构的部分,强调以下几点:
· 在整个Java开发领域之中,没有一本真正去讲解数据结构的书,只能去看C语言的数据结构;
· 在所有的开发之中,都会存在数据结构的身影,可以这样去解释:数据结构的精通与否,完全决定于以后;
· 数据结构的核心:引用数据类型操作。
3.1.1、链表的基本概念
链表实际上可以理解为一串数据,或者按照专业性的说法,可以理解为动态的对象数组,对象数组最大优点:是表示出多的概念,例如:多个雇员。但是传统的对象数组有一个最大的问题在于,里面保存的数据的长度是固定的。
思考:如果说现在要想扩大一个对象数组的范围?
· 建立一个新的对象数组,而后将原本的内容拷贝到新数组之中,再改变原数组的引用方式。
public class TestDemo {
public static void main(String args[]) {
String [] str = new String[] {"Hello","World","MLDN"} ;
String [] newStr = new String[6] ;
System.arraycopy(str,0,newStr,0,str.length) ;
str = newStr ; // 改变引用,存在垃圾
str [3] = "你好" ;
str [4] = "世界" ;
for (int x = 0 ; x < str.length ; x ++) {
System.out.println(str[x]) ;
}
}
}
通过以上的分析,可以发现,对象数组所有的对象元素被数组的索引控制,可以说是自动完成的控制,但是链表需要人为进行关系的设置,而且每个操作设置的时候,除了要保存“对象”之外,还要再多保留一个引用。
这个引用就和之前讲解的领导是一样的:一个雇员的领导还是雇员,雇员领导的领导也是雇员。
范例:先给出链表的基本操作结构
class Node {
private String data ; // 假设要保存的数据类型是字符串
private Node next ;
public Node(String data) {
this.data = data ;
}
public String getData() {
return this.data ;
}
public void setNext(Node next) {
this.next = next ;
}
public Node getNext() {
return this.next ;
}
}
范例:挂节点
public class TestDemo {
public static void main(String args[]) {
// 1、设置数据
Node n1 = new Node("火车头") ;
Node n2 = new Node("车厢A") ;
Node n3 = new Node("车厢B") ;
n1.setNext(n2) ;
n2.setNext(n3) ;
// 2、取出数据
Node currentNode = n1 ; // 设置每一个当前节点
while(currentNode != null) { // 有节点存在
System.out.println(currentNode.getData()) ; // 当前节点内容
currentNode = currentNode.getNext() ;
}
}
}
但是这样的方式来输出所有的节点的配置过程,发现并不是特别好,这种输出的操作,应该采用递归合适。
public class TestDemo {
public static void main(String args[]) {
// 1、设置数据
Node n1 = new Node("火车头") ;
Node n2 = new Node("车厢A") ;
Node n3 = new Node("车厢B") ;
n1.setNext(n2) ;
n2.setNext(n3) ;
// 2、取出数据
print(n1) ;
}
public static void print(Node node) { // 取出节点内容
System.out.println(node.getData()) ;
if (node.getNext() != null) { // 后面还有货
print(node.getNext()) ; // 向下
}
}
}
可以发现,整个一链表的关键是在于Node节点的关系匹配上。
3.1.2、链表的基本雏形
通过之前的分析,可以发现链表的最大作用的类就是Node,但是以上程序都是由用户自己去匹配节点关系的,但是这些节点的匹配工作不应该由用户完成,应该由一个程序专门负责。
那么专门负责这个节点操作的类,就称为链表类 —— Link,负责处理节点关系,而用户不需要关心节点问题,只需要关心Link的处理操作即可。
范例:开发Link类
class Node {
private String data ; // 假设要保存的数据类型是字符串
private Node next ;
public Node(String data) {
this.data = data ;
}
public String getData() {
return this.data ;
}
public void setNext(Node next) {
this.next = next ;
}
public Node getNext() {
return this.next ;
}
public void addNode(Node newNode) { // 操作的是节点关系
if (this.next == null) { // 当前节点的next为null
this.next = newNode ; // 保存新节点
} else {
this.next.addNode(newNode) ;
}
}
public void printNode() {
System.out.println(this.data) ;
if (this.next != null) { // 还有下一个节点
this.next.printNode() ;
}
}
}
class Link { // 处理节点关系
private Node root ; // 根节点
public void add(String data) { // 处理数据保存
if (data == null) { // 没有数据
return ; // 直接返回
}
// 每一个数据如果要想保存在链表之中,必须将其封装为节点
// 这一操作的过程外部(用户)不需要知道
Node newNode = new Node(data) ;
if (this.root == null) { // 现在没有根节点
this.root = newNode ; // 第一个作为根节点
} else {
this.root.addNode(newNode) ;
}
}
public void print() {
if (this.root != null) { // 现在有根节点
this.root.printNode() ; // Node类处理
}
}
}
public class TestDemo {
public static void main(String args[]) {
Link all = new Link() ;
all.add("Hello") ;
all.add("World") ;
all.add("MLDN") ;
all.print() ;
}
}
通过以上的代码可以发现,Link类处理节点的操作,而Node类负责节点的具体顺序的操作,但是客户端,不关心节点,只关心Link类即可。
3.1.3、开发可用链表
3.1.3.1、增加数据:public String add(数据对象)
通过上面的程序分析,可以发现,对于链表实现,Node类是整个操作的关键,但是首先来研究一下之前程序的问题:Node是一个单独的类,那么这样的类是可以被用户直接使用的,但是这个类由用户直接去使用,没有任何的意义,即:这个类有用,但是不能让用户去用,只能让Link类去使用,内部类完成。
class Link { // 用户唯一关注的是此类
// 使用内部类的最大好处是可以和外部类进行私有操作的互相访问
private class Node { // 处理节点关系
private String data ; // 要保存的数据
private Node next ; // 下一个节点
public Node(String data){
this.data = data ;
}
public void addNode(Node newNode) { // 增加节点
if (this.next == null) { // 当前节点之后没有节点
this.next = newNode ; // 保存新节点
} else { // 当前节点之后有节点了
this.next.addNode(newNode) ; // 向下继续判断
}
}
}
private Node root ; // 根节点,第一个保存元素
public boolean add(String data) { // 增加数据
if (data == null) { // 如果保存的是一个空数据
return false ; // 增加失败
}
// 将数据封装为节点,目的:节点有next可以处理关系
Node newNode = new Node(data) ;
// 链表的关键就在于根节点
if (this.root == null) { // 现在没有根节点
this.root = newNode ; // 第一个作为根节点
} else { // 根节点有了,新的节点要保留在合适的位置
this.root.addNode(newNode) ; // Node类负责处理
}
return true ; // 增加成功
}
}
使用内部类可以发现比之前的代码要节省一些,而且访问也方便了。
3.1.3.2、增加多个数据:public boolean addAll(数据对象 [] )
以上的操作是每次增加了一个对象,那么如果现在要求增加多个对象呢,例如:增加对象数组。可以采用循环数组的方式,每次调用add()方法。
public boolean addAll(String data[]) { // 一组数据
for (int x = 0 ; x < data.length ; x ++) {
if (!this.add(data[x])) { // 保存不成功
return false ;
}
}
return true ;
}
3.1.3.3、统计数据个数:public int size()
在一个链表之中,会保存多个数据(每一个数据都被封装为Node类对象),那么要想取得这些保存元素的个数,可以增加一个size()方法完成。
应该在Link类之中增加一个统计的属性:count:
private int count ; // 统计个数
当用户每一次调用add()方法增加新数据的时候应该做出统计:
public boolean add(String data) { // 增加数据
if (data == null) { // 如果保存的是一个空数据
return false ; // 增加失败
}
// 将数据封装为节点,目的:节点有next可以处理关系
Node newNode = new Node(data) ;
// 链表的关键就在于根节点
if (this.root == null) { // 现在没有根节点
this.root = newNode ; // 第一个作为根节点
} else { // 根节点有了,新的节点要保留在合适的位置
this.root.addNode(newNode) ; // Node类负责处理
}
this.count ++ ; // 保存数据量增加
return true ; // 增加成功
}
而在size()方法就是简单的将count这个变量的内容返回:
public int size() {
return this.count ;
}
3.1.3.4、判断是否是空链表:public boolean isEmpty()
所谓的空链表指的是链表之中不保存任何的数据,实际上这个null可以通过两种方式判断:一种判断链表的根节点是否为null,另外一个是判断保存元素的个数是否为0。
public boolean isEmpty() {
return this.count == 0 ;
}
3.1.3.5、查找数据是否存在:public boolean contains(数据对象)
现在如果要想查询某个数据是否存在,那么基本的操作原理:逐个盘查,盘查的具体实现还是应该交给Node类去处理,但是在盘查之前必须有一个前提:有数据存在。
在Link类之中,增加查询的操作:
public boolean contains(String data) { // 查找数据
// 根节点没有数据,查找的也没有数据
if (this.root == null || data == null) {
return false ; // 不需要进行查找了
}
return this.root.containsNode(data) ; // 交给Node类处理
}
在Node类之中,完成具体的查询,查询的流程:
·判断当前节点的内容是否满足于查询内容,如果满足返回true;
·如果当前节点的内容不满足,则向后继续查,如果已经没有后续节点了,则返回false。
public boolean containsNode(String data) { // 查找数据
if (data.equals(this.data)) { // 与当前节点数据吻合
return true ;
} else { // 与当前节点数据不吻合
if (this.next != null) { // 还有下一个节点
return this.next.containsNode(data) ;
} else { // 没有后续节点
return false ; // 查找不到
}
}
}
3.1.3.6、删除数据:public void remove(数据对象)
对于链表之中的内容,之前完成的是增加操作和查询操作,但是从链表之中也会存在删除数据的操作,可是删除数据的操作需要分两种情况讨论:
· 情况一:删除的数据不是根节点,使用:要删除节点的上一个节点.next = 要删除节点.next;
·情况二:删除的数据是根节点,根节点 = 根节点.next。
范例:修改Link类的删除操作
public void remove(String data) { // 要删除的节点
if (! this.contains(data)) { // 要删除的数据不存在
return ; // 直接返回被调用处,下面代码不执行了
}
if (data.equals(this.root.data)) { // 要删除的是根节点
this.root = this.root.next ; // 根节点的下一个
} else { // 要删除的不是根节点
this.root.next.removeNode(this.root,data) ;
}
this.count -- ; // 修改个数
}
范例:修改Node类的删除操作
// 传入两个参数:上一个节点,另外一个表示要删除的数据
public void removeNode(Node previous,String data) {
if (this.data.equals(data)) { // 当前节点的数据吻合删除条件
previous.next = this.next ; // 空出当前节点
} else {
this.next.removeNode(this,data) ; // 向后继续删除
}
}
3.1.3.7、取出全部数据:public 数据 [] toArray()
对于链表的这种数据结构,最为关键的是两个操作:删除、取得全部数据。
在Link类之中需要定义一个操作数组的脚标:
private int foot = 0 ; // 操作返回数组的脚标
要把数据保存的数组,Link类和Node类都需要使用,那么可以在Link类中定义返回数组,必须以属性的形式出现,只有这样,Node类才可以访问这个数组并进行操作。
private String [] retData ; // 返回数组
在Link类之中增加toArray()的方法。
public String [] toArray() {
if (this.count == 0) {
return null ; // 没有数据
}
this.foot = 0 ; // 清零
this.retData = new String [this.count] ; // 开辟数组大小
this.root.toArrayNode() ;
return this.retData ;
}
修改Node类的操作,增加toArrayNode()方法。
public void toArrayNode() {
Link.this.retData[Link.this.foot ++] = this.data ;
if (this.next != null) {
this.next.toArrayNode() ;
} }
不过,按照以上的方式进行开发,每一次调用toArray()方法,都要重复的进行数据的遍历,如果在数据没有修改的情况下,这种做法是一种非常差的做法,最好的做法是增加一个修改标记,如果发现数据增加了或删除的话,表示要重新遍历数据。
private boolean changeFlag = true ;
// changeFlag == true:数据被更改了,则需要重新遍历
// changeFlag == false:数据没有更改,不需要重新遍历
当增加或删除数据的时候,这个标记必须要进行修改。
public String [] toArray() {
if (this.count == 0) {
return null ; // 没有数据
}
this.foot = 0 ; // 清零
if (this.changeFlag == true) { // 内容被修改了,需要重新取
this.retData = new String [this.count] ; // 开辟数组大小
this.root.toArrayNode() ;
}
return this.retData ; }
3.1.3.8、根据索引位置取得数据:public 数据 get(int index)
在一个链表之中会有多个节点保存数据,现在要求可以取得指定节点位置上的数据。但是在进行这一操作的过程之中,有一个小问题:如果要取得数据的索引超过了数据的保存个数,那么是无法取得的。
在Link类之中,增加一个get()方法。
public String get(int index) {
if (index > this.count) { // 超过个数
return null ; // 返回null
}
this.foot = 0 ; // 操作foot来定义脚标
return this.root.getNode(index) ;
}
在Node类之中配置getNode()方法。
public String getNode(int index) {
if (Link.this.foot ++ == index) { // 当前索引为查找数值
return this.data ;
} else {
return this.next.getNode(index) ;
}
}
3.1.3.9、清空链表:public void clear()
所有的链表被root拽着,这个时候如果root为null,那么后面的数据都会断开,就表示都成了垃圾。
public void clear() {
this.root = null ;
this.count = 0 ;
}
在整个程序之中,对于代码的开发部分不要求可以从无到有编写,但是需要清楚其操作原理,并且可以修改,而在整个链表工具类之中的方法:
No.
方法名称
类型
描述
1
public String add(数据 对象)
普通
向链表之中增加一个数据
2
public boolean addAll(数据 对象 [] )
普通
向链表之中增加一组数据
3
public int size()
普通
取得链表之中的元素个数
4
public boolean isEmpty()
普通
判断链表是否为空
5
public boolean contains(数据 对象)
普通
从链表之中查询数据,依靠对象比较
6
public void remove(数据 对象)
普通
从链表中删除数据,依靠对象比较
7
public 数据 [] toArray()
普通
将链表之中保存的数据以对象数组的方式返回
8
public 数据 get(int index)
普通
根据索引位置取得指定的对象
9
public void clear()
普通
清空链表
以上的九个方法的作用,一定要清楚。
3.1.4、保存多个对象
之前的链表程序保存的都是String型数据,但是现在也可以是一个用户自己定义的类。
如果是自定义的类,那么一定要考虑到对象比较的方法,因为在链表之中的,contains()、remove()两个方法都调用了字符串内容的比较操作,而如果是一个自定义的Java类呢?需要编写compare()方法(对象比较)。
在之前学习了关系模型:dept– emp的操作,那么按照这个思路的话,链表可以针对于Emp使用,在一个部门有多个雇员,只要是多个就是对象数组,但是对象数组肯定以动态对象数组为主,那么一定就是链表的应用。
3.2、第四个代码模型的练习(重点)
重申:关于链表的掌握程度。
· 奢侈的想法:每个人可以自己独立的编写链表;
· 实际的想法:可以使用链表,修改链表为自己的类型(类型、对象比较操作)。
3.2.1、第一道练习,一对多
商品表:名称、价格、描述。
class Link { // 用户唯一关注的是此类
// 使用内部类的最大好处是可以和外部类进行私有操作的互相访问
private class Node { // 处理节点关系
private Product data ; // 要保存的数据
private Node next ; // 下一个节点
public Node(Product data){
this.data = data ;
}
public void addNode(Node newNode) { // 增加节点
if (this.next == null) { // 当前节点之后没有节点
this.next = newNode ; // 保存新节点
} else { // 当前节点之后有节点了
this.next.addNode(newNode) ; // 向下继续判断
}
}
public boolean containsNode(Product data) { // 查找数据
if (data.compare(this.data)) { // 与当前节点数据吻合
return true ;
} else { // 与当前节点数据不吻合
if (this.next != null) { // 还有下一个节点
return this.next.containsNode(data) ;
} else { // 没有后续节点
return false ; // 查找不到
}
}
}
// 传入两个参数:上一个节点,另外一个表示要删除的数据
public void removeNode(Node previous,Product data) {
if (this.data.compare(data)) { // 当前节点的数据吻合删除条件
previous.next = this.next ; // 空出当前节点
} else {
this.next.removeNode(this,data) ; // 向后继续删除
}
}
public void toArrayNode() {
Link.this.retData[Link.this.foot ++] = this.data ;
if (this.next != null) {
this.next.toArrayNode() ;
}
}
public Product getNode(int index) {
if (Link.this.foot ++ == index) { // 当前索引为查找数值
return this.data ;
} else {
return this.next.getNode(index) ;
}
}
}
private Node root ; // 根节点,第一个保存元素
private int count = 0 ; // 统计个数
private int foot = 0 ; // 操作返回数组的脚标
private Product [] retData ; // 返回数组
private boolean changeFlag = true ;
// changeFlag == true:数据被更改了,则需要重新遍历
// changeFlag == false:数据没有更改,不需要重新遍历
public boolean add(Product data) { // 增加数据
if (data == null) { // 如果保存的是一个空数据
return false ; // 增加失败
}
// 将数据封装为节点,目的:节点有next可以处理关系
Node newNode = new Node(data) ;
// 链表的关键就在于根节点
if (this.root == null) { // 现在没有根节点
this.root = newNode ; // 第一个作为根节点
} else { // 根节点有了,新的节点要保留在合适的位置
this.root.addNode(newNode) ; // Node类负责处理
}
this.count ++ ; // 保存数据量增加
this.changeFlag = true ; // 被修改了
return true ; // 增加成功
}
public boolean addAll(Product data[]) { // 一组数据
for (int x = 0 ; x < data.length ; x ++) {
if (!this.add(data[x])) { // 保存不成功
return false ;
}
}
return true ;
}
public int size() {
return this.count ;
}
public boolean isProductty() {
return this.count == 0 ;
}
public boolean contains(Product data) { // 查找数据
// 根节点没有数据,查找的也没有数据
if (this.root == null || data == null) {
return false ; // 不需要进行查找了
}
return this.root.containsNode(data) ; // 交给Node类处理
}
public void remove(Product data) { // 要删除的节点
if (! this.contains(data)) { // 要删除的数据不存在
return ; // 直接返回被调用处,下面代码不执行了
}
if (data.equals(this.root.data)) { // 要删除的是根节点
this.root = this.root.next ; // 根节点的下一个
} else { // 要删除的不是根节点
this.root.next.removeNode(this.root,data) ;
}
this.changeFlag = true ; // 被修改了
this.count -- ; // 修改个数
}
public Product [] toArray() {
if (this.count == 0) {
return null ; // 没有数据
}
this.foot = 0 ; // 清零
if (this.changeFlag == true) { // 内容被修改了,需要重新取
this.retData = new Product [this.count] ; // 开辟数组大小
this.root.toArrayNode() ;
}
return this.retData ;
}
public Product get(int index) {
if (index > this.count) { // 超过个数
return null ; // 返回null
}
this.foot = 0 ; // 操作foot来定义脚标
return this.root.getNode(index) ;
}
public void clear() {
this.root = null ;
this.count = 0 ;
}
}
class ProductGroup {
private int pgid ;
private String title ;
private String note ;
private Link products = new Link();
public ProductGroup() {}
public ProductGroup(int pgid,String title,String note) {
this.pgid = pgid ;
this.title = title ;
this.note = note ;
}
public void setProducts(Link products) {
this.products = products ;
}
public Link getProducts() {
return this.products ;
}
public String getProductGroupInfo() {
return "商品组编号:" + this.pgid + ",名称:" + this.title + ",描述:" + this.note ;
}
}
class Product {
private int pid ;
private String title ;
private double price ;
private String note ;
private ProductGroup group ;
public Product(){}
public Product(int pid,String title,double price,String note){
this.pid = pid ;
this.title = title ;
this.price = price ;
this.note = note ;
}
public boolean compare(Product product) {
if (this == product) {
return true ;
}
if (product == null) {
return false ;
}
if (this.pid == product.pid
&& this.title.equals(product.title)
&& this.price == product.price
&& this.note.equals(product.note)) {
return true ;
}
return false ;
}
public void setGroup(ProductGroup group) {
this.group = group ;
}
public ProductGroup getGroup() {
return this.group ;
}
public String getProductInfo() {
return "商品编号:" + this.pid + ",名称:" + this.title + ",价格:" + this.price + ",描述:" + this.note ;
}
}
public class TestDemo {
public static void main(String args[]) {
// 一层设置关系
ProductGroup group = new ProductGroup(1,"生活用品","你懂的。。。。") ;
group.getProducts().add(new Product(10,"毛巾",1.0,"你懂的。")) ;
group.getProducts().add(new Product(11,"香皂",1.5,"你懂的。")) ;
group.getProducts().add(new Product(12,"牙刷",0.3,"你懂的。")) ;
System.out.println(group.getProductGroupInfo()) ;
Product prod [] = group.getProducts().toArray() ;
for (int x = 0 ; x < prod.length ; x ++) {
System.out.println(prod[x].getProductInfo()) ;
}
}
}
3.2.2、第二道练习,一对多
要求1:根据一个用户找到他的所有定单;
要求2:根据一个定单找到其下单的用户;
class Link { // 用户唯一关注的是此类
// 使用内部类的最大好处是可以和外部类进行私有操作的互相访问
private class Node { // 处理节点关系
private Orders data ; // 要保存的数据
private Node next ; // 下一个节点
public Node(Orders data){
this.data = data ;
}
public void addNode(Node newNode) { // 增加节点
if (this.next == null) { // 当前节点之后没有节点
this.next = newNode ; // 保存新节点
} else { // 当前节点之后有节点了
this.next.addNode(newNode) ; // 向下继续判断
}
}
public boolean containsNode(Orders data) { // 查找数据
if (data.compare(this.data)) { // 与当前节点数据吻合
return true ;
} else { // 与当前节点数据不吻合
if (this.next != null) { // 还有下一个节点
return this.next.containsNode(data) ;
} else { // 没有后续节点
return false ; // 查找不到
}
}
}
// 传入两个参数:上一个节点,另外一个表示要删除的数据
public void removeNode(Node previous,Orders data) {
if (this.data.compare(data)) { // 当前节点的数据吻合删除条件
previous.next = this.next ; // 空出当前节点
} else {
this.next.removeNode(this,data) ; // 向后继续删除
}
}
public void toArrayNode() {
Link.this.retData[Link.this.foot ++] = this.data ;
if (this.next != null) {
this.next.toArrayNode() ;
}
}
public Orders getNode(int index) {
if (Link.this.foot ++ == index) { // 当前索引为查找数值
return this.data ;
} else {
return this.next.getNode(index) ;
}
}
}
private Node root ; // 根节点,第一个保存元素
private int count = 0 ; // 统计个数
private int foot = 0 ; // 操作返回数组的脚标
private Orders [] retData ; // 返回数组
private boolean changeFlag = true ;
// changeFlag == true:数据被更改了,则需要重新遍历
// changeFlag == false:数据没有更改,不需要重新遍历
public boolean add(Orders data) { // 增加数据
if (data == null) { // 如果保存的是一个空数据
return false ; // 增加失败
}
// 将数据封装为节点,目的:节点有next可以处理关系
Node newNode = new Node(data) ;
// 链表的关键就在于根节点
if (this.root == null) { // 现在没有根节点
this.root = newNode ; // 第一个作为根节点
} else { // 根节点有了,新的节点要保留在合适的位置
this.root.addNode(newNode) ; // Node类负责处理
}
this.count ++ ; // 保存数据量增加
this.changeFlag = true ; // 被修改了
return true ; // 增加成功
}
public boolean addAll(Orders data[]) { // 一组数据
for (int x = 0 ; x < data.length ; x ++) {
if (!this.add(data[x])) { // 保存不成功
return false ;
}
}
return true ;
}
public int size() {
return this.count ;
}
public boolean isOrdersty() {
return this.count == 0 ;
}
public boolean contains(Orders data) { // 查找数据
// 根节点没有数据,查找的也没有数据
if (this.root == null || data == null) {
return false ; // 不需要进行查找了
}
return this.root.containsNode(data) ; // 交给Node类处理
}
public void remove(Orders data) { // 要删除的节点
if (! this.contains(data)) { // 要删除的数据不存在
return ; // 直接返回被调用处,下面代码不执行了
}
if (data.equals(this.root.data)) { // 要删除的是根节点
this.root = this.root.next ; // 根节点的下一个
} else { // 要删除的不是根节点
this.root.next.removeNode(this.root,data) ;
}
this.changeFlag = true ; // 被修改了
this.count -- ; // 修改个数
}
public Orders [] toArray() {
if (this.count == 0) {
return null ; // 没有数据
}
this.foot = 0 ; // 清零
if (this.changeFlag == true) { // 内容被修改了,需要重新取
this.retData = new Orders [this.count] ; // 开辟数组大小
this.root.toArrayNode() ;
}
return this.retData ;
}
public Orders get(int index) {
if (index > this.count) { // 超过个数
return null ; // 返回null
}
this.foot = 0 ; // 操作foot来定义脚标
return this.root.getNode(index) ;
}
public void clear() {
this.root = null ;
this.count = 0 ;
}
}
class User {
private String userid ;
private String password ;
private String email ;
private String mobile ;
private int points ;
private Link orders = new Link() ;
public User() {}
public User(String userid,String password,String email,String mobile,int points) {
this.userid = userid ;
this.password = password ;
this.email = email ;
this.mobile = mobile ;
this.points = points ;
}
public void setOrders(Link orders) {
this.orders = orders ;
}
public Link getOrders() {
return this.orders ;
}
public String getUserInfo() {
return "用户ID:" + this.userid + ",密码:" + this.password + ",email:" + this.email + ",电话:" + this.mobile + ",积分:" + this.points ;
}
}
class Orders {
private int oid ;
private double allPrice ;
private User user ;
public Orders() {}
public Orders(int oid,double allPrice) {
this.oid = oid ;
this.allPrice = allPrice ;
}
public void setUser(User user) {
this.user = user ;
}
public User getUser() {
return this.user ;
}
public boolean compare(Orders orders) {
if (this == orders) {
return true ;
}
if (orders == null) {
return false ;
}
if (this.oid == orders.oid
&& this.allPrice == orders.allPrice) {
return true ;
}
return false ;
}
public String getOrdersInfo() {
return "定单编号:" + this.oid + ",总价:" + this.allPrice ;
}
}
public class TestDemo {
public static void main(String args[]) {
User user = new User("mldn","nihao","mldnqa@163.com","110",10) ;
Orders o1 = new Orders(10,100.0) ;
Orders o2 = new Orders(11,200.0) ;
Orders o3 = new Orders(12,300.0) ;
user.getOrders().add(o1) ;
user.getOrders().add(o2) ;
user.getOrders().add(o3) ;
o1.setUser(user) ;
o2.setUser(user) ;
o3.setUser(user) ;
System.out.println(user.getUserInfo()) ;
System.out.println(user.getOrders().toArray()[0].getOrdersInfo()) ;
System.out.println(user.getOrders().toArray()[0].getUser().getUserInfo()) ;
}}
3.2.3、第三道练习,简单多对多(了解)
多对多的映射关系在开发之中是存在两种的:
· 情况一:关系表之中,只存在关联字段,不存在任何的其他字段,留的题目属于简单多对多;
· 情况二:关系表之中,存在着其他的操作字段,数据模型:一个学生可以参加多门课程,每门课程可以有多个学生参加,每个学生针对于每门课程有一个成绩,在关系表中存在了一个成绩字段;
多对多 = 两个一对多。
管理员-管理员组-权限,简单多对多;
要求1:根据一个管理员的信息可以找到这个管理员所在的所有管理员组,并且列出每个管理员组的权限;
要求2:根据一个管理员组可以列出这个管理员组的权限,以及所有的管理员;
要求3:根据一个权限可以找到具备此权限的所有管理员组,并且列出每个管理员组的所有管理员。
class AdminLink { // 用户唯一关注的是此类
// 使用内部类的最大好处是可以和外部类进行私有操作的互相访问
private class Node { // 处理节点关系
private Admin data ; // 要保存的数据
private Node next ; // 下一个节点
public Node(Admin data){
this.data = data ;
}
public void addNode(Node newNode) { // 增加节点
if (this.next == null) { // 当前节点之后没有节点
this.next = newNode ; // 保存新节点
} else { // 当前节点之后有节点了
this.next.addNode(newNode) ; // 向下继续判断
}
}
public boolean containsNode(Admin data) { // 查找数据
if (data.compare(this.data)) { // 与当前节点数据吻合
return true ;
} else { // 与当前节点数据不吻合
if (this.next != null) { // 还有下一个节点
return this.next.containsNode(data) ;
} else { // 没有后续节点
return false ; // 查找不到
}
}
}
// 传入两个参数:上一个节点,另外一个表示要删除的数据
public void removeNode(Node previous,Admin data) {
if (this.data.compare(data)) { // 当前节点的数据吻合删除条件
previous.next = this.next ; // 空出当前节点
} else {
this.next.removeNode(this,data) ; // 向后继续删除
}
}
public void toArrayNode() {
AdminLink.this.retData[AdminLink.this.foot ++] = this.data ;
if (this.next != null) {
this.next.toArrayNode() ;
}
}
public Admin getNode(int index) {
if (AdminLink.this.foot ++ == index) { // 当前索引为查找数值
return this.data ;
} else {
return this.next.getNode(index) ;
}
}
}
private Node root ; // 根节点,第一个保存元素
private int count = 0 ; // 统计个数
private int foot = 0 ; // 操作返回数组的脚标
private Admin [] retData ; // 返回数组
private boolean changeFlag = true ;
// changeFlag == true:数据被更改了,则需要重新遍历
// changeFlag == false:数据没有更改,不需要重新遍历
public boolean add(Admin data) { // 增加数据
if (data == null) { // 如果保存的是一个空数据
return false ; // 增加失败
}
// 将数据封装为节点,目的:节点有next可以处理关系
Node newNode = new Node(data) ;
// 链表的关键就在于根节点
if (this.root == null) { // 现在没有根节点
this.root = newNode ; // 第一个作为根节点
} else { // 根节点有了,新的节点要保留在合适的位置
this.root.addNode(newNode) ; // Node类负责处理
}
this.count ++ ; // 保存数据量增加
this.changeFlag = true ; // 被修改了
return true ; // 增加成功
}
public boolean addAll(Admin data[]) { // 一组数据
for (int x = 0 ; x < data.length ; x ++) {
if (!this.add(data[x])) { // 保存不成功
return false ;
}
}
return true ;
}
public int size() {
return this.count ;
}
public boolean isAdminty() {
return this.count == 0 ;
}
public boolean contains(Admin data) { // 查找数据
// 根节点没有数据,查找的也没有数据
if (this.root == null || data == null) {
return false ; // 不需要进行查找了
}
return this.root.containsNode(data) ; // 交给Node类处理
}
public void remove(Admin data) { // 要删除的节点
if (! this.contains(data)) { // 要删除的数据不存在
return ; // 直接返回被调用处,下面代码不执行了
}
if (data.equals(this.root.data)) { // 要删除的是根节点
this.root = this.root.next ; // 根节点的下一个
} else { // 要删除的不是根节点
this.root.next.removeNode(this.root,data) ;
}
this.changeFlag = true ; // 被修改了
this.count -- ; // 修改个数
}
public Admin [] toArray() {
if (this.count == 0) {
return null ; // 没有数据
}
this.foot = 0 ; // 清零
if (this.changeFlag == true) { // 内容被修改了,需要重新取
this.retData = new Admin [this.count] ; // 开辟数组大小
this.root.toArrayNode() ;
}
return this.retData ;
}
public Admin get(int index) {
if (index > this.count) { // 超过个数
return null ; // 返回null
}
this.foot = 0 ; // 操作foot来定义脚标
return this.root.getNode(index) ;
}
public void clear() {
this.root = null ;
this.count = 0 ;
}
}
class GroupLink { // 用户唯一关注的是此类
// 使用内部类的最大好处是可以和外部类进行私有操作的互相访问
private class Node { // 处理节点关系
private Group data ; // 要保存的数据
private Node next ; // 下一个节点
public Node(Group data){
this.data = data ;
}
public void addNode(Node newNode) { // 增加节点
if (this.next == null) { // 当前节点之后没有节点
this.next = newNode ; // 保存新节点
} else { // 当前节点之后有节点了
this.next.addNode(newNode) ; // 向下继续判断
}
}
public boolean containsNode(Group data) { // 查找数据
if (data.compare(this.data)) { // 与当前节点数据吻合
return true ;
} else { // 与当前节点数据不吻合
if (this.next != null) { // 还有下一个节点
return this.next.containsNode(data) ;
} else { // 没有后续节点
return false ; // 查找不到
}
}
}
// 传入两个参数:上一个节点,另外一个表示要删除的数据
public void removeNode(Node previous,Group data) {
if (this.data.compare(data)) { // 当前节点的数据吻合删除条件
previous.next = this.next ; // 空出当前节点
} else {
this.next.removeNode(this,data) ; // 向后继续删除
}
}
public void toArrayNode() {
GroupLink.this.retData[GroupLink.this.foot ++] = this.data ;
if (this.next != null) {
this.next.toArrayNode() ;
}
}
public Group getNode(int index) {
if (GroupLink.this.foot ++ == index) { // 当前索引为查找数值
return this.data ;
} else {
return this.next.getNode(index) ;
}
}
}
private Node root ; // 根节点,第一个保存元素
private int count = 0 ; // 统计个数
private int foot = 0 ; // 操作返回数组的脚标
private Group [] retData ; // 返回数组
private boolean changeFlag = true ;
// changeFlag == true:数据被更改了,则需要重新遍历
// changeFlag == false:数据没有更改,不需要重新遍历
public boolean add(Group data) { // 增加数据
if (data == null) { // 如果保存的是一个空数据
return false ; // 增加失败
}
// 将数据封装为节点,目的:节点有next可以处理关系
Node newNode = new Node(data) ;
// 链表的关键就在于根节点
if (this.root == null) { // 现在没有根节点
this.root = newNode ; // 第一个作为根节点
} else { // 根节点有了,新的节点要保留在合适的位置
this.root.addNode(newNode) ; // Node类负责处理
}
this.count ++ ; // 保存数据量增加
this.changeFlag = true ; // 被修改了
return true ; // 增加成功
}
public boolean addAll(Group data[]) { // 一组数据
for (int x = 0 ; x < data.length ; x ++) {
if (!this.add(data[x])) { // 保存不成功
return false ;
}
}
return true ;
}
public int size() {
return this.count ;
}
public boolean isGroupty() {
return this.count == 0 ;
}
public boolean contains(Group data) { // 查找数据
// 根节点没有数据,查找的也没有数据
if (this.root == null || data == null) {
return false ; // 不需要进行查找了
}
return this.root.containsNode(data) ; // 交给Node类处理
}
public void remove(Group data) { // 要删除的节点
if (! this.contains(data)) { // 要删除的数据不存在
return ; // 直接返回被调用处,下面代码不执行了
}
if (data.equals(this.root.data)) { // 要删除的是根节点
this.root = this.root.next ; // 根节点的下一个
} else { // 要删除的不是根节点
this.root.next.removeNode(this.root,data) ;
}
this.changeFlag = true ; // 被修改了
this.count -- ; // 修改个数
}
public Group [] toArray() {
if (this.count == 0) {
return null ; // 没有数据
}
this.foot = 0 ; // 清零
if (this.changeFlag == true) { // 内容被修改了,需要重新取
this.retData = new Group [this.count] ; // 开辟数组大小
this.root.toArrayNode() ;
}
return this.retData ;
}
public Group get(int index) {
if (index > this.count) { // 超过个数
return null ; // 返回null
}
this.foot = 0 ; // 操作foot来定义脚标
return this.root.getNode(index) ;
}
public void clear() {
this.root = null ;
this.count = 0 ;
}
}
class PrivilegeLink { // 用户唯一关注的是此类
// 使用内部类的最大好处是可以和外部类进行私有操作的互相访问
private class Node { // 处理节点关系
private Privilege data ; // 要保存的数据
private Node next ; // 下一个节点
public Node(Privilege data){
this.data = data ;
}
public void addNode(Node newNode) { // 增加节点
if (this.next == null) { // 当前节点之后没有节点
this.next = newNode ; // 保存新节点
} else { // 当前节点之后有节点了
this.next.addNode(newNode) ; // 向下继续判断
}
}
public boolean containsNode(Privilege data) { // 查找数据
if (data.compare(this.data)) { // 与当前节点数据吻合
return true ;
} else { // 与当前节点数据不吻合
if (this.next != null) { // 还有下一个节点
return this.next.containsNode(data) ;
} else { // 没有后续节点
return false ; // 查找不到
}
}
}
// 传入两个参数:上一个节点,另外一个表示要删除的数据
public void removeNode(Node previous,Privilege data) {
if (this.data.compare(data)) { // 当前节点的数据吻合删除条件
previous.next = this.next ; // 空出当前节点
} else {
this.next.removeNode(this,data) ; // 向后继续删除
}
}
public void toArrayNode() {
PrivilegeLink.this.retData[PrivilegeLink.this.foot ++] = this.data ;
if (this.next != null) {
this.next.toArrayNode() ;
}
}
public Privilege getNode(int index) {
if (PrivilegeLink.this.foot ++ == index) { // 当前索引为查找数值
return this.data ;
} else {
return this.next.getNode(index) ;
}
}
}
private Node root ; // 根节点,第一个保存元素
private int count = 0 ; // 统计个数
private int foot = 0 ; // 操作返回数组的脚标
private Privilege [] retData ; // 返回数组
private boolean changeFlag = true ;
// changeFlag == true:数据被更改了,则需要重新遍历
// changeFlag == false:数据没有更改,不需要重新遍历
public boolean add(Privilege data) { // 增加数据
if (data == null) { // 如果保存的是一个空数据
return false ; // 增加失败
}
// 将数据封装为节点,目的:节点有next可以处理关系
Node newNode = new Node(data) ;
// 链表的关键就在于根节点
if (this.root == null) { // 现在没有根节点
this.root = newNode ; // 第一个作为根节点
} else { // 根节点有了,新的节点要保留在合适的位置
this.root.addNode(newNode) ; // Node类负责处理
}
this.count ++ ; // 保存数据量增加
this.changeFlag = true ; // 被修改了
return true ; // 增加成功
}
public boolean addAll(Privilege data[]) { // 一组数据
for (int x = 0 ; x < data.length ; x ++) {
if (!this.add(data[x])) { // 保存不成功
return false ;
}
}
return true ;
}
public int size() {
return this.count ;
}
public boolean isPrivilegety() {
return this.count == 0 ;
}
public boolean contains(Privilege data) { // 查找数据
// 根节点没有数据,查找的也没有数据
if (this.root == null || data == null) {
return false ; // 不需要进行查找了
}
return this.root.containsNode(data) ; // 交给Node类处理
}
public void remove(Privilege data) { // 要删除的节点
if (! this.contains(data)) { // 要删除的数据不存在
return ; // 直接返回被调用处,下面代码不执行了
}
if (data.equals(this.root.data)) { // 要删除的是根节点
this.root = this.root.next ; // 根节点的下一个
} else { // 要删除的不是根节点
this.root.next.removeNode(this.root,data) ;
}
this.changeFlag = true ; // 被修改了
this.count -- ; // 修改个数
}
public Privilege [] toArray() {
if (this.count == 0) {
return null ; // 没有数据
}
this.foot = 0 ; // 清零
if (this.changeFlag == true) { // 内容被修改了,需要重新取
this.retData = new Privilege [this.count] ; // 开辟数组大小
this.root.toArrayNode() ;
}
return this.retData ;
}
public Privilege get(int index) {
if (index > this.count) { // 超过个数
return null ; // 返回null
}
this.foot = 0 ; // 操作foot来定义脚标
return this.root.getNode(index) ;
}
public void clear() {
this.root = null ;
this.count = 0 ;
}
}
class Admin {
private String adminid ;
private String password ;
private GroupLink groups = new GroupLink() ;
public Admin() {}
public Admin(String adminid,String password) {
this.adminid = adminid ;
this.password = password ;
}
public GroupLink getGroups() {
return this.groups ;
}
public String getAdminInfo() {
return "管理员ID:" + this.adminid + ",密码:" + this.password ;
}
public boolean compare(Admin admin) {
if (this == admin) {
return true ;
}
if (admin == null) {
return false ;
}
if (this.adminid.equals(admin.adminid)
&& this.password.equals(admin.password)) {
return true ;
}
return false ;
}
}
class Group {
private int groupid ;
private String title ;
private String note ;
private AdminLink admins = new AdminLink() ;
private PrivilegeLink privileges = new PrivilegeLink() ;
public Group() {}
public Group(int groupid,String title,String note) {
this.groupid = groupid ;
this.title = title ;
this.note = note ;
}
public AdminLink getAdmins() {
return this.admins ;
}
public PrivilegeLink getPrivileges() {
return this.privileges ;
}
public String getGroupInfo() {
return "管理员组ID:" + this.groupid + ",名称:" + this.title + ",描述:" + this.note ;
}
public boolean compare(Group group) {
if (this == group) {
return true ;
}
if (group == null) {
return false ;
}
if (this.groupid == group.groupid
&& this.title.equals(group.title)
&& this.note.equals(group.note)) {
return true ;
}
return false ;
}
}
class Privilege {
private int pid ;
private String title ;
private String note ;
private GroupLink groups = new GroupLink() ;
public Privilege() {}
public Privilege(int pid,String title,String note) {
this.pid = pid ;
this.title = title ;
this.note = note ;
}
public GroupLink getGroups() {
return this.groups ;
}
public String getPrivilegeInfo() {
return "权限ID:" + this.pid + ",名称:" + this.title + ",描述:" + this.note ;
}
public boolean compare(Privilege privilege) {
if (this == privilege) {
return true ;
}
if (privilege == null) {
return false ;
}
if (this.pid == privilege.pid
&& this.title.equals(privilege.title)
&& this.note.equals(privilege.note)) {
return true ;
}
return false ;
}
}
public class TestDemo {
public static void main(String args[]) {
// 一层配置关系
Admin adminA = new Admin("admininstrator","admin") ;
Admin adminB = new Admin("hello","hello") ;
Admin adminC = new Admin("jijiyiyi","jy") ;
Admin adminD = new Admin("yushi","yushi") ;
Admin adminE = new Admin("world","world") ;
Group groupA = new Group(10,"超级管理员组","你懂的。") ;
Group groupB = new Group(11,"系统维护管理员组","你懂的。") ;
Group groupC = new Group(12,"信息发布管理员组","你懂的。") ;
Privilege p1 = new Privilege(101,"增加管理员","-") ;
Privilege p2 = new Privilege(102,"增加用户","-") ;
Privilege p3 = new Privilege(103,"权限分配","-") ;
Privilege p4 = new Privilege(104,"增加新闻","-") ;
Privilege p5 = new Privilege(105,"生成统计报表","-") ;
Privilege p6 = new Privilege(106,"删除用户","-") ;
Privilege p7 = new Privilege(107,"评论维护","-") ;
Privilege p8 = new Privilege(108,"修改新闻","-") ;
Privilege p9 = new Privilege(109,"审核发布","-") ;
// 配置管理员和管理员组关系
adminA.getGroups().add(groupA) ;
adminA.getGroups().add(groupB) ;
adminB.getGroups().add(groupA) ;
adminB.getGroups().add(groupB) ;
adminB.getGroups().add(groupC) ;
adminC.getGroups().add(groupA) ;
adminC.getGroups().add(groupC) ;
adminD.getGroups().add(groupB) ;
adminE.getGroups().add(groupB) ;
groupA.getAdmins().add(adminA) ;
groupA.getAdmins().add(adminB) ;
groupA.getAdmins().add(adminC) ;
groupB.getAdmins().add(adminA) ;
groupB.getAdmins().add(adminB) ;
groupB.getAdmins().add(adminD) ;
groupB.getAdmins().add(adminE) ;
groupC.getAdmins().add(adminB) ;
groupC.getAdmins().add(adminC) ;
// 配置管理员组和权限
groupA.getPrivileges().add(p1) ;
groupA.getPrivileges().add(p2) ;
groupA.getPrivileges().add(p3) ;
groupA.getPrivileges().add(p4) ;
groupA.getPrivileges().add(p5) ;
groupB.getPrivileges().add(p1) ;
groupB.getPrivileges().add(p2) ;
groupB.getPrivileges().add(p3) ;
groupB.getPrivileges().add(p4) ;
groupB.getPrivileges().add(p5) ;
groupB.getPrivileges().add(p6) ;
groupB.getPrivileges().add(p7) ;
groupB.getPrivileges().add(p8) ;
groupB.getPrivileges().add(p9) ;
groupC.getPrivileges().add(p4) ;
groupC.getPrivileges().add(p5) ;
groupC.getPrivileges().add(p6) ;
groupC.getPrivileges().add(p7) ;
groupC.getPrivileges().add(p8) ;
groupC.getPrivileges().add(p9) ;
p1.getGroups().add(groupA) ;
p1.getGroups().add(groupB) ;
p2.getGroups().add(groupA) ;
p2.getGroups().add(groupB) ;
p3.getGroups().add(groupA) ;
p3.getGroups().add(groupB) ;
p4.getGroups().add(groupA) ;
p4.getGroups().add(groupB) ;
p4.getGroups().add(groupC) ;
p5.getGroups().add(groupA) ;
p5.getGroups().add(groupB) ;
p5.getGroups().add(groupC) ;
p6.getGroups().add(groupB) ;
p6.getGroups().add(groupC) ;
p7.getGroups().add(groupB) ;
p7.getGroups().add(groupC) ;
p8.getGroups().add(groupB) ;
p8.getGroups().add(groupC) ;
p9.getGroups().add(groupB) ;
p9.getGroups().add(groupC) ;
// 要求1:根据一个管理员的信息可以找到这个管理员所在的所有管理员组,并且列出每个管理员组的权限;
{
System.out.println(adminA.getAdminInfo()) ;
Group group [] = adminA.getGroups().toArray() ;
for (int x = 0 ; x < group.length ; x ++) {
System.out.println("\t〖管理员组〗" + group[x].getGroupInfo()) ;
Privilege pri [] = group[x].getPrivileges().toArray() ;
for (int y = 0 ; y < pri.length ; y ++) {
System.out.println("\t\t【权限】" + pri[y].getPrivilegeInfo()) ;
}
}
System.out.println("**************************************************************************") ;
}
// 要求2:根据一个管理员组可以列出这个管理员组的权限,以及所有的管理员;
{
System.out.println(groupA.getGroupInfo()) ;
Admin admin [] = groupA.getAdmins().toArray() ;
for (int x = 0 ; x < admin.length ; x ++) {
System.out.println("\t〖管理员〗" + admin[x].getAdminInfo()) ;
}
Privilege pri [] = groupA.getPrivileges().toArray() ;
for (int x = 0 ; x < pri.length ; x ++) {
System.out.println("\t【权 限】" + pri[x].getPrivilegeInfo()) ;
}
System.out.println("**************************************************************************") ;
}
// 要求3:根据一个权限可以找到具备此权限的所有管理员组,并且列出每个管理员组的所有管理员。
{
System.out.println(p3.getPrivilegeInfo()) ;
Group group [] = p3.getGroups().toArray() ;
for (int x = 0 ; x < group.length ; x ++) {
System.out.println("\t〖管理员组〗" + group[x].getGroupInfo()) ;
Admin admin [] = group[x].getAdmins().toArray() ;
for (int y = 0 ; y < admin.length ; y ++) {
System.out.println("\t\t【管理员】" + admin[y].getAdminInfo()) ;
}
}
}
}
}
4、总结
1、 链表可以不会,但是必须会使;
2、 链表这个数据结构的增加、和输出操作必须会;
3、 数据模型的建立,第四个代码模型的完善。
5、预习任务
继承、final关键字、方法覆写、对象多态性、抽象类和接口。
- Java面向对象04
- 面向对象04-java基础
- JAVA面向对象练习04
- Java基础知识04-面向对象
- java----Java面向对象
- java----Java面向对象
- java----Java面向对象
- java----Java面向对象
- java----Java面向对象
- java----Java面向对象
- 【java】java面向对象
- 【Java】Java面向对象
- java面向对象(一) 了解面向对象
- 【学习笔记】java面向对象复习04
- Java SE 04 面向对象-下
- Java基础知识04-面向对象-类
- Java面向对象概述
- java面向对象
- Java面向对象01
- Java面向对象02
- Oracle_11g的服务启动与关闭
- Java模块 -- Logger日志的使用
- Java面向对象03
- Java面向对象04
- Java面向对象05
- < Kotlin > Android Studio3.0 新特性 ~ New Features in Android Studio Preview (译文)
- http协议
- Java面向对象06
- 青蛙的约会
- JSON
- HTML基本介绍与简单标签
- 设计模式分类,23种设计模式