由于数据本身不具备先后的关系,所以使用Node类来封装数据,同时利用Node来指向下一个节点。
1 简单链表的实现
节点类(Node):
package com.test;
/**
* @author 1
* 定义节点类Node
*/
public class Node {
private String data ; //保存数据
private Node next ; //要保存下一个节点
public Node(String data){ //默认要传入数据创建节点
this.data = data ;
this.next = null ;
}
public void setNext(Node next){ //设置下一个节点
this.next = next ;
}
public String getData() { //取出当前对象的值
return this.data;
}
public Node getNext() { //取出下一个节点
return this.next;
}
}
数据测试:
package com.test;
public class LinkTest {
public static void main(String[] args) {
//准备出所有的数据
Node root = new Node("链表头");
Node n1 = new Node("数据1");
Node n2 = new Node("数据2");
//设置数据之间的先后关系
root.setNext(n1);
n1.setNext(n2);
//取出所有数据
//循环取出
Node currentNote = root ;
while (currentNote != null ) { //当前节点有数据
System.out.println(currentNote.getData());
//将下一个对象 设置为当前对象
currentNote = currentNote.getNext();
}
//递归取出
print(root);
}
public static void print(Node current){
if (current == null) {
return ;
}
System.out.println(current.getData());
print(current.getNext());
}
}
这样写,结果没有问题,但是客户端做的事情太过多了,用户在操作的过程中要关心Node类是否存在,所有的节点的关系需要用户去处理,所以我们修改。
2 简单链表改进
针对上面的问题,我们需要定义一个工具来帮助客户端去隐藏所有的链表中给出的细节操作。添加Link工具类。
节点类:
package com.test;
/**
* @author 1
* 定义节点类Node
*/
public class Node {
private String data ; //保存数据
private Node next ; //要保存下一个节点
public Node(String data){ //默认要传入数据创建节点
this.data = data ;
this.next = null ;
}
public void setNext(Node next){ //设置下一个节点
this.next = next ;
}
public String getData() { //取出当前对象的值
return this.data;
}
public Node getNext() { //取出下一个节点
return this.next;
}
/**
* 实现节点的添加
* 第一次调用:this = Link.root
* 第二次调用:this = Link.root.next
*/
public void addNode(Node newNode){
//当前节点的下一个节点为空
if(this.next == null){
this.next = newNode;
return ;
} else {
//当前节点的下一个节点继续保存
this.next.addNode(newNode);
}
}
/**
* 实现节点的输出
* 第一次调用:this = Link.root
* 第二次调用:this = Link.root.next
*/
public void printNode() {
System.out.println(this.data); //输出当前的数据
//下一个节点还存在
if (this.next != null) {
this.next.printNode();
} else {
return ;
}
}
}
Link类:
package com.test;
/**
* @author 1
* 负责数据的设置、输出
* 处理Node的关系
*/
public class Link {
private Node root ; //设置根节点
/**
* @param data 添加的数据
*/
public void add (String data){
//为了设置数据的先后关系,所以我们要把data保存在Node对象里
Node newNode = new Node(data);
//第一次添加数据,root不存在
if (this.root == null) {
this.root = newNode ;
} else { //root存在了
//随后的添加交给Node处理
//从root节点后找到相应的位置
this.root.addNode(newNode);
}
}
public void print(){
if (this.root != null) { //存在根节点,交给Node输出
this.root.printNode();
}
}
}
数据测试类:
package com.test;
public class LinkTest {
public static void print(Node current){
if (current == null) {
return ;
}
System.out.println(current.getData());
print(current.getNext());
}*/
public static void main(String[] args) {
Link link = new Link();
link.add("hello");
link.add("world");
link.add("JAVA");
link.print();
}
}
现在这样设计,客户端不用去关注具体的Node以及引用的关系的细节,只关注了Link提供的方法;同时Link类的主要功能是控制Node类对象的产生和根节点的使用;Node类注重的是数据 的保存与引用 关系的分配。这样每个类的各做各的事。但是现在还有问题,因为Node类主要是给Link类来操作,但是现在我们可跳过Link类,直接去操作Node类,这样是没有任何意义存在的,所以我必须修改我们的设计结构,让Node 类只能让Link类使用,所以我们使用内部类来实现。
3 用内部类来实现链表
package com.inner;
public class Link {
private class Node{ //定义的内部类,私有,防止别的类跳过Link来创建Node对象
private String data ;
private Node next ;
public Node(String data ){
this.data = data ;
this.next = null ;
}
public void addNode(Node newNode) {
if (this.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();
} else {
return ;
}
}
}
private Node root ;
public void add(String data){
Node newNode = new Node(data);
if (this.root == null) {
this.root = newNode;
} else {
this.root.addNode(newNode);
}
}
public void print(){
if (this.root != null) { //存在根节点,交给Node输出
this.root.printNode();
}
}
}
现在设计的结构已经可以说是定下来了,现在需要的是对功能的增加。
4 链表功能的完善
package com.inner;
public class Link {
private class Node { // 定义的内部类,私有,防止别的类跳过Link来创建Node对象
private String data;
private Node next;
public Node(String data) {
this.data = data;
this.next = null;
}
public void addNode(Node newNode) {
if (this.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();
} else {
return;
}
}
public boolean containsNode(String data){
if (data.equals(this.data)) {
return true;
} else {
if (this.next == null) {
//没有后继结点
return false;
} else {
return this.next.containsNode(data);
}
}
}
public String getNode(int index) {
if(Link.this.foot ++ == index){
return this.data;
} else {
return this.getNode(index);
}
}
public void setNode(int index, String data) {
if(Link.this.foot ++ == index){
this.data = data;
//当条件满足,返回
return ;
} else {
this.setNode(index,data);
}
}
/**
* @param previous 要删除的上一个结点
* @param data 要删除的内容
*/
public void removeNode(Node previous,String data){
if (data.equals(this.data)) {
previous.next = this.next ;
Link.this.length --; //修改链表大小
} else {
this.next.removeNode(this, data);
}
}
/**
* node转换成数组
*/
public void toArrayNode() {
Link.this.elementDatas[Link.this.foot++] = this.data;
if (this.next != null) {
this.next.toArrayNode();
}
}
}
private Node root;
// 记录添加的个数
private int length = 0;
//链表转换成数组下标,链表就是动态的对象数组
private int foot = 0 ;
//返回的数组属性,方便Node类操作
private String[] elementDatas;
/**
* 向链表添加数据
*
* @param data 添加元素的数据
*/
public void add(String data) {
// 不保存null
if (data == null) {
return;
}
Node newNode = new Node(data);
if (this.root == null) {
this.root = newNode;
} else {
this.root.addNode(newNode);
}
this.length++;
}
/**
* 输出链表
*/
public void print() {
if (this.root != null) { // 存在根节点,交给Node输出
this.root.printNode();
}
}
/**
* @return length 保存的数据量
*/
public int size() {
return length;
}
/**
* 判断是不是空
*/
public boolean isEmpty() {
return this.length == 0 ;
}
/**
* @param data 要查询的数据
* @return 存在返回true
*/
public boolean contains(String data){
if (data == null || this.root == null) {
return false ;
}
return this.root.containsNode(data);
}
/**
* @param index 要取元素的下标,从0开始
* @return
*/
public String get(int index){
if (index > this.length) {
return null;
}
//将对应数组的下标重置回0
this.foot = 0;
return this.root.getNode(index);
}
/**
* @param index 要修改元素的下标
* @param data 修改的内容
*/
public void set(int index , String data){
if (index > this.length) {
return ;
}
//将对应数组的下标重置回0
this.foot = 0;
this.root.setNode(index,data);
}
/**
* @param data 要删除的数据
* 有两种情况,要删除的是根结点,要删除的是非根结点
*/
public void remove(String data){
//判断数据是不是存在
if (this.contains(data)) {
//若没有使用内部类,这里处理起来会很复杂
if(data.equals(this.root.data)){
//是根节点
this.root = this.root.next;
this.length --; //修改链表大小
} else {
this.root.next.removeNode(this.root, data);
}
}
}
/**
* 把链表转换成数组
* @return
*/
public String[] toArray(){
if (this.root == null) {
return null;
}
this.foot = 0 ; //脚标控制
this.elementDatas = new String[this.length]; //根据链表的大小来创建数组
this.root.toArrayNode(); //交给Nod有类处理
return this.elementDatas;
}
}
常用的功能已经基本上完成,但是这个链表基本上不能使用,因为我们的数据类型不可能只有String类型,而上面的代码是必须是String才可以正常使用。
5 通用链表
package com.inner;
public class Link<t> {
private class Node { // 定义的内部类,私有,防止别的类跳过Link来创建Node对象
private T data;
private Node next;
public Node(T data) {
this.data = data;
this.next = null;
}
public void addNode(Node newNode) {
if (this.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();
} else {
return;
}
}
public boolean containsNode(T data){
if (data.equals(this.data)) {
return true;
} else {
if (this.next == null) {
//没有后继结点
return false;
} else {
return this.next.containsNode(data);
}
}
}
public T getNode(int index) {
if(Link.this.foot ++ == index){
return this.data;
} else {
return this.getNode(index);
}
}
public void setNode(int index, T data) {
if(Link.this.foot ++ == index){
this.data = data;
//当条件满足,返回
return ;
} else {
this.setNode(index,data);
}
}
/**
* @param previous 要删除的上一个结点
* @param data 要删除的内容
*/
public void removeNode(Node previous,T data){
if (data.equals(this.data)) {
previous.next = this.next ;
Link.this.length --; //修改链表大小
} else {
this.next.removeNode(this, data);
}
}
/**
* node转换成数组
*/
public void toArrayNode() {
Link.this.elementDatas[Link.this.foot++] = this.data;
if (this.next != null) {
this.next.toArrayNode();
}
}
}
private Node root;
// 记录添加的个数
private int length = 0;
//链表转换成数组下标,链表就是动态的对象数组
private int foot = 0 ;
//返回的数组属性,方便Node类操作
private Object[] elementDatas;
/**
* 向链表添加数据
*
* @param data 添加元素的数据
*/
public void add(T data) {
// 不保存null
if (data == null) {
return;
}
Node newNode = new Node(data);
if (this.root == null) {
this.root = newNode;
} else {
this.root.addNode(newNode);
}
this.length++;
}
/**
* 输出链表
*/
public void print() {
if (this.root != null) { // 存在根节点,交给Node输出
this.root.printNode();
}
}
/**
* @return length 保存的数据量
*/
public int size() {
return length;
}
/**
* 判断是不是空
*/
public boolean isEmpty() {
return this.length == 0 ;
}
/**
* @param data 要查询的数据
* @return 存在返回true
*/
public boolean contains(T data){
if (data == null || this.root == null) {
return false ;
}
return this.root.containsNode(data);
}
/**
* @param index 要取元素的下标,从0开始
* @return
*/
public T get(int index){
if (index > this.length) {
return null;
}
//将对应数组的下标重置回0
this.foot = 0;
return this.root.getNode(index);
}
/**
* @param index 要修改元素的下标
* @param data 修改的内容
*/
public void set(int index , T data){
if (index > this.length) {
return ;
}
//将对应数组的下标重置回0
this.foot = 0;
this.root.setNode(index,data);
}
/**
* @param data 要删除的数据
* 有两种情况,要删除的是根结点,要删除的是非根结点
*/
public void remove(T data){
//判断数据是不是存在
if (this.contains(data)) {
//若没有使用内部类,这里处理起来会很复杂
if(data.equals(this.root.data)){
//是根节点
this.root = this.root.next;
this.length --; //修改链表大小
} else {
this.root.next.removeNode(this.root, data);
}
}
}
/**
* 把链表转换成数组
* @return
*/
public Object[] toArray(){
if (this.root == null) {
return null;
}
this.foot = 0 ; //脚标控制
this.elementDatas = new Object[this.length]; //根据链表的大小来创建数组
this.root.toArrayNode(); //交给Nod有类处理
return this.elementDatas;
}
}
</t>
这里使用了泛型来处理不同的数据类型的问题,同时修改了返回数组的返回值修改成了Object类型。