定义一个接口
public interface ILinarList<E> {
public abstract boolean add(E item); //添加元素
public abstract boolean add(int i,E item); //插入元素
public abstract E remove(int i); //删除元素
public abstract int indexOf(E item); //定位元素
public abstract E get(int i); //取表元素
public abstract int size(); //求线性表长度
public abstract void clear(); //清空线性表
public abstract boolean isEmpty(); //判断线性表是否为空
}
定义一个结点类
/*
* 单链表的的原理就在结点这里,在每一个结点中都包含一个下一个结点的地址,
* 如果想要访问下一个结点,就要通过上一个结点来调用他的成员变量next,以此类推
*/
public class Node<E> {
E item;//创建一个泛型类item
Node<E> next;//创建一个结点类类型
public Node(E element,Node <E> next) {
this.item=element;
this.next=next;
}
}
创建一个SLinkList类来实现接口
public class SLinkList<E> implements ILinarList<E>{
private Node<E> start;//1 新建一个空的带头节点
int size;
public SLinkList(){
start=null;
size=0;
}
/*一个节点中的成员变量指向一个新的Node,从实例对象的角度,整条链表中有实例化的对象为start,
* 详见1、2,可以看出,private Node<E> start=new Node<E>(item,null),实例化了一个start
* 接下来add方法中,Node<E> current=start 为引用了start 的地址,start 的成员变量next的类型是Node<E>的引用类型,
* 里面包含一个E类型的item,和一个Node<E>类型的next
* 也就是说每添加一个节点都是没有实例化对象的,直接从start 的next成员指向一个新的Node<E>,
* 再添加就是从新的Node<E>中的next成员指向新的Node<E>
* 如下面的语句
*/
//添加元素到链表的末尾
public boolean add(E item) {
if(start==null)//如果带头节点为空,将新建一个带头节点
start=new Node<E>(item,null);//2
else {//如果带头节点不为空的话,新建一个current指向start的地址
Node<E> current=start;
while(current.next!=null){//依次访问每一个节点,当一个节点的next为空时,说明此节点为最后一个节点
current=current.next;//注意,这里的赋值,每次都是赋予地址,并不是修改
}
current.next=new Node<E>(item,null);//引用最后一个节点的next,创建一个新的节点
}
size++;
return true;
}
//插入元素到i索引位置前插入一个数据元素
public boolean add(int i, E item) {
if(i<0||i>size)//等于size时候即为在末尾调加一个元素
throw new IndexOutOfBoundsException("Index:"+i+",Size:"+size);//抛出越界异常
Node<E> newnode=new Node<E>(item, null);
if(i==0){//第一个元素前插入一个元素
newnode.next=start;//newnode.next指向start的地址,此时原头元素有了一个指向他的地址
start=newnode;//start的地址指向newnode的地址,所以此时有两个实例对象指向带头元素,分别是start和newnode
size++;
}else{
Node<E> current=start;//将current指向头元素的地址
Node<E> previous=null;
int number=0;//定义一个number用来计算访问节点的次数
while(number<i){//从0开始自加,加到i时候,此时current指向i的索引位置,previous指向前一个
previous=current;//previous始终保存current前一个节点的地址
current=current.next;//循环访问current的next的地址
number++;//每访问一次number自加一次
}
previous.next=newnode;//将前一个节点的地址的next指向修改为newnode
newnode.next=current;//将newnode的next指向当前的节点元素
size++;
}
return true;
}
//清楚索引的元素
public E remove(int i) {
E oidValue=null;//先赋值为null,两个循环都没有进入时,此方法返回一个null
if(i<0||i>size-1)
throw new IndexOutOfBoundsException("Index:"+i+",Size:"+size);//抛出越界异常
else if(!isEmpty()){//当整个链表不为空时,进去此语句
if(i==0){//当要清楚头元素时
oidValue=start.item;//将返回元素赋值
start=start.next;
size--;
}else{//以下方法同add重写的第二次方法
Node<E> previous=null;
Node<E> current=start;
int number=0;
while(number<i){
previous=current;
current=current.next;
number++;
}
oidValue=current.item;
previous.next=current.next;//将指定元素的前一个元素的next指向指定元素的下一个元素
current.next=null;//释放当前元素的next
current=null;//同上(个人觉得不做这两步好像也没有什么问题?)
size--;
}
}
return oidValue;
}
public int indexOf(E item) {
int number=0;
if(item==null){//需要单独对item==null的情况判定,因equals没有比较null,空指针错误
for(Node<E> x=start;x!=null;x=x.next){
if(x.item==null)
return number;
number++;
}
}else{
for(Node<E>x=start;x!=null;x=x.next){
if(item.equals(x.item))//判定是否为所要的元素
return number;
number++;
}
}
return -1;//当遍历完整个链表之后没有所要的元素,说明此链表不存在此元素,返回-1
}
public E get(int i) {
E item=null;
if(i<0||i>size-1)
throw new IndexOutOfBoundsException("Index:"+i+",Size:"+size);//抛出越界异常
else if(!isEmpty()){
Node<E> current=start;
for(int j=0;j<i;j++)//当知道要取第i个元素的时候,用for循环执行i次
current=current.next;
item=current.item;
}
return item;
}
public int size() {
return size;
}
public void clear() {
for(Node<E> x=start;x!=null;){//当x为空时停止
Node<E> next=x.next;//保存x.next的地址
x.item=null;//将x中的元素清空
x.next=null;//将x的指向清空,注意,x.next不代表是下一个节点,仅仅是指向下一个节点的地址
x=next;
}
start =null;//start也是指向一片地址,如果没有清空的话,依然可以访问,只不过start.item和start.next都为空罢了
size=0;
}
public boolean isEmpty() {
return size==0;
}
}