4.返回链表数据
链表中所有的数据都通过Node封装后实现了动态保存,并且取消了数组长度的限制。但是保存在链表中的数据也需要被外界获取,那么此时就可以利用数组的形式返回链表中的保存数据。考虑到此功能的通用性,所以返回的数组类型应该是Object,操作结构如图。
在进行链表数据获取时,应该根据当前链表所保存的集合长度开辟相应的数组,随后利用索引的方式从链表中取得相应的数据并将其保存在数组中。
(1)【ILink】在ILink接口中追加方法用于返回链表数据。
/**
* 获取链表中的全部内容,该内容将以数组的形式返回
* @return 如果链表有内容则返回与保存元素个数相当的数组,如果没有内容则返回null
*/
public Object[] toArray();
(2)【LinkImpl】在LinkImpl子类中定义两个成员属性,用于返回数组声明与数组索引控制。
private int foot; //数组操作脚标
private Object[] returnData; //返回数据保存
(3)【LinkImpl.Node】在Node类中追加新的方法,通过递归的形式将链表中的数据保存在数组中。
/**
* 将链表中的全部元素保存到对象数组中
*/
public void toArrayNode(){
//将当前节点的数据取出保存到returnData数组中,同时进行索引自增
LinkImpl.this.returnData[LinkImpl.this.foot++]=this.data;
if(this.next !=null){ //还有下一个数据
this.next.toArrayNode(); //递归调用
}
}
(4)【LinkImpl】覆写ILink接口中的toArray()方法。
@Override
public Object[] toArray(){
if(this.isEmpty()){ //空集合
throw new NullPointerException("集合内容为空");
}
this.foot=0; //脚标清零
this.returnData=new Object[this.count]; //根据已有长度开辟数组
this.root.toArrayNode(); //利用Node类进行递归数据获取
return this.returnData; //返回全部元素
}
(5)【测试类】编写测试程序调用toArray()方法。
public class Main{
public static void main(String args[]){
ILink<String>link=new LinkImpl<String>(); //实例化链表对象
link.add("浩汉"); //链表中保存数据
link.add("浩渺");
link.add("JOE");
Object results[]=link.toArray(); //获取全部保存数据
for(Object obj:results){
String str=(String) obj; //确定为String类型,强制转型
System.out.println(str+"、") //输出对象
}
}
}
完整代码如下:
package cn.kuiba.util;
interface ILink<E>{
public void add(E e);
public int size();
public boolean isEmpty();
public Object[] toArray();
}
class LinkImpl<E> implements ILink<E>{
private int foot;
private Object[] retuenData;
private int count;
private class Node<E>{
private E data;
private Node<E> next;
public Node(E data){
this.data=data;
}
public void addNode(Node<E> newNode){
if (this.next == null){
this.next=newNode;
}else {
this.next.addNode(newNode);
}
}
public void toArrayNode(){
LinkImpl.this.retuenData[LinkImpl.this.foot++]=this.data;
if (this.next != null){
this.next.toArrayNode();
}
}
}
private Node<E> root;
@Override
public void add(E e){
if (e == null){
return;
}
Node<E> newNode=new Node<E>(e);
if (this.root == null){
this.root=newNode;
}else {
this.root.addNode(newNode);
}
this.count++;
}
@Override
public int size(){
return this.count;
}
@Override
public Object[] toArray(){
if (this.isEmpty()){
throw new NullPointerException("集合内容为空");
}
this.foot=0;
this.retuenData=new Object[this.count];
this.root.toArrayNode();
return this.retuenData;
}
@Override
public boolean isEmpty(){
return this.count==0;
}
}
public class Main {
public static void main(String args[]){
ILink<String>link=new LinkImpl<String>();
System.out.println("数据保存前链表元素个数:"+link.size());
link.add("浩汉");
link.add("浩渺");
link.add("JOE");
System.out.println("数据保存后链表元素个数:"+link.size());
Object results[]= link.toArray();
for (Object obj:results){
String str=(String) obj;
System.out.println(str+"、");
}
}
}
程序执行结果:
数据保存前链表元素个数:0
数据保存后链表元素个数:3
浩汉、
浩渺、
JOE、
本程序通过链表中的toArray()方法可以将保存在链表中的数据全部取出,就可以利用foreach实现内容打印。
5.根据索引取得数据
传统数组和链表都是基于顺序式的形式实现了数据的保存,所以链表也可以利用索引的形式通过递归获取指定数据,操作如图
(1)【ILink】在ILink接口中定义新的方法可以根据索引获取数据。
/**
* 根据索引获取链表中的指定元素内容
* @param index要获取元素的索引
* @return 指定索引的位置的数据
*/
public E get(int index);
(2)【LinkImpl.Node】在Node类中追加索引获取数据的方法,此时可以利用LinkImpl类中的foot进行索引判断。
/**
* 根据节点索引获取元素
* @param index 要获取的索引编号,该索引编号一定是有效编号
* @return 索引对应的数据
*/
public E getNode(int index){
if(LinkImpl.this.foot++ == index){ //索引相同
return this.data; //返回当前数据
}else{ //继续向后获取数据
return this.next.getNode(index);
}
}
(3)【LinkImpl】在LinkImpl子类中覆写get()方法。
@Override
public E get(int index){
if(index >= this.count){ //索引不在指定范围内
throw new ArrayIndexOutOfBoundsException("不正确的数据索引");
}
this.foot=0; //重置索引的下标
return this.foot.getNode(index); //交由Node类查找
}
(4)【测试类】编写测试程序,调用get()方法。
public class Main{
public static void main(String args[]){
Ilink<String>link=new LinkImpl<String>(); //实例化链表对象
link.add("浩汉"); //链表中保存数据
link.add("浩渺");
link.add("JOE");
System.out.println(link.get(1)); //获取第2个元素
System.out.println(link.get(3)); //错误的索引
}
}
package cn.kuiba.util;
interface ILink<E>{
public void add(E e);
public int size();
public boolean isEmpty();
public Object[] toArray();
public E get(int index);
}
class LinkImpl<E> implements ILink<E>{
private int foot;
private Object[] returnData;
private int count;
private class Node<E>{
public E getNode(int index){
if (LinkImpl.this.foot++==index){
return this.data;
}else {
return this.next.getNode(index);
}
}
private E data;
private Node<E> next;
public Node(E data){
this.data=data;
}
public void addNode(Node<E> 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();
}
}
}
private Node<E> root;
@Override
public void add(E e){
if (e == null){
return;
}
Node<E> newNode=new Node<E>(e);
if (this.root == null){
this.root=newNode;
}else {
this.root.addNode(newNode);
}
this.count++;
}
@Override
public int size(){
return this.count;
}
@Override
public Object[] toArray(){
if (this.isEmpty()){
throw new NullPointerException("集合内容为空");
}
this.foot=0;
this.returnData=new Object[this.count];
this.root.toArrayNode();
return this.returnData;
}
@Override
public boolean isEmpty(){
return this.count==0;
}
@Override
public E get(int index){
if (index>=this.count){
throw new ArrayIndexOutOfBoundsException("不正确的数据索引");
}
this.foot=0;
return this.root.getNode(index);
}
}
public class Main {
public static void main(String args[]) {
ILink<String> link = new LinkImpl<String>();
System.out.println("数据保存前链表元素个数:" + link.size());
link.add("浩汉");
link.add("浩渺");
link.add("JOE");
System.out.println("数据保存后链表元素个数:" + link.size());/*
Object results[] = link.toArray();
for (Object obj : results) {
String str = (String) obj;
System.out.println(str + "、");*/
System.out.println(link.get(1));
System.out.println(link.get(3));
}
}
//}
程序执行结果:数据保存前链表元素个数:0
数据保存后链表元素个数:3
浩渺
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 不正确的数据索引
at cn.kuiba.util.LinkImpl.get(Main.java:76)
at cn.kuiba.util.Main.main(Main.java:95)
本程序在get()方法中由于存在索引的检查机制,所以一旦使用了不正确的索引将会产生相应的异常提示。
6.修改链表结构
链表中的数据由于存在foot这个成员变量就可以通过索引的形式来进行操作,利用索引可以实现内容的修改。
(1)【ILink】在ILink接口中追加数据修改方法。
/**
* 修改指定索引中的数据内容
* @param index要修改的数据索引
* @param data要替换的新内容
*/
public void set(int index,E data);
(2)【LinkImpl.Node】在Node类中增加一个索引数据修改的方法。
/**
* 修改指定索引中的数据内容
* @param index要修改的数据索引
* @param data要替换的新内容
*/
public void setNode(int index,E data){
if (LinkImpl.this.foot++==index){ //索引相同
this.data=data; //修改数据
}else {
this.next.setNode(index,data); //后续节点操作
}
}
(3)【LinkImpl】在LinkImpl子类中覆写set()方法。
@Override
public void set(int index ,E data){
if (index>=this.count){ //索引不在指定的范围内
throw new ArrayIndexOutOfBoundsException("不正确的数据索引");
}
this.foot=0; //重置索引的下标
this.root.setNode(index,data); //Node类修改数据
}
(4)【测试类】编写程序实现内容修改。
public class Main {
public static void main(String args[]) {
ILink<String> link = new LinkImpl<String>(); //实例化链表对象
link.add("浩汉"); //链表中保存数据
link.add("浩渺");
link.add("JOE");
link.set(1, "世界"); //修改内容
System.out.println(link.get(1)); //获取第2个元素
}
}
程序执行结果:
世界
本程序利用了set()方法修改了指定索引的内容,随后利用get()方法获取索引数据,实质上set()和get()两个方法的实现原理相同。