单向链表结构
其中,引用变量next,head分别存储节点在堆区中的地址。
接口设计
在动态数组ArrayList和链表LinkedList中,有很多相同功能但不同实现的方法,因此将这些方法提取出来构成一个接口List,用于给这两个类实现。
方法 | 功能 |
---|---|
int size() | 返回元素数量 |
boolean isEmpty() | 是否为空 |
boolean contains(E element) | 是否包含element元素 |
int indexOf(E element) | 返回element元素(可以为空)在数组中的索引,不存在则返回-1 |
void clear() | 清空所有元素 |
void add(E element) | 将element插入到数组末端 |
void add(int index,E element) | 将element插入到索引index位置 |
E remove(int index) | 删除索引为index处的元素 |
E get(int index) | 获取索引为index处的元素 |
E set(int index,E element) | 将index处的元素修改为element |
List接口:
package com.mj.LinkListP;
public interface List<E> {
//表示未找到该元素的常量
static final int ELEMENT_NOT_FOUND=-1;
/**
* 返回元素数量
* @return
*/
public int size();
/**
* 是否为空
* @return
*/
public boolean isEmpty();
/**
* 是否包含element元素
* @param element
* @return
*/
public boolean contains(E element);
/**
* 返回element元素(可以为空)在数组中的索引,不存在则返回-1
* @param element
* @return
*/
public int indexOf(E element);
/**
* 清空所有元素
*/
public void clear();
/**
* 将element插入到数组末端
* @param element
*/
public void add(E element);
/**
* 将element插入到索引index位置
* @param index
* @param element
*/
public void add(int index,E element);
/**
* 删除索引为index处的元素
* @param index
* @return 被删除的元素
*/
public E remove(int index);
/**
* 获取索引为index处的元素
* @param index
* @return
*/
public E get(int index);
/**
* 将index处的元素修改为element
* @param index
* @param element
* @return 被替换的元素
*/
public E set(int index,E element);
}fangf
抽象类设计
然而ArrayList和LinkedList还有一些相同功能且相同实现的方法或者变量可以共用,但接口中的方法不能有方法体,因此构建一个抽象类AbstractList继承List接口,实现一些共有的方法供子类使用。
方法、变量声明 | 功能 |
---|---|
int size | 元素数量 |
int size() | 返回动态数组大小 |
boolean isEmpty() | 是否为空 |
boolean contains(E element) | 是否包含element元素 |
void add(E element) | 将element插入到数组末端 |
void throwException(int index) | 抛出索引越界异常 |
void rangeCheck(int index) | 判断索引的合理性 |
void rangeCheckForAdd(int index) | 为Add方法检查索引合理性 |
AbstractList:
public abstract class AbstractList<E> implements List<E>{
//元素数量
protected int size=0;
/**
* 返回动态数组大小
* @return
*/
public int size(){
return size;
}
/**
* 是否为空
* @return
*/
public boolean isEmpty(){
return size==0;
}
/**
* 是否包含element元素
* @param element
* @return
*/
public boolean contains(E element){
return indexOf(element)!=ELEMENT_NOT_FOUND;
}
/**
* 将element插入到数组末端
* @param element
*/
public void add(E element){
add(size,element);
}
/**
* 抛出索引越界异常
* @param index
*/
protected void throwException(int index){
throw new IndexOutOfBoundsException("index:"+index+" , size:"+size);
}
/**
* 判断索引的合理性
* @param index
*/
protected void rangeCheck(int index){
if(index<0||index>=size)
throwException(index);
}
//为Add方法检查索引合理性
protected void rangeCheckForAdd(int index){
if(index<0||index>size)//对于Add方法可以在size处添加元素
throwException(index);
}
在此的LinkedLlist的继承关系为:
LinkedList实现细节
public class LinkedList<E> extends AbstractList<E>{
//定义普通内部类Node
private class Node<E>{
private E element;
private Node<E> next;
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
private Node<E> first;//头节点
@Override
public int indexOf(E element) {
Node<E> node=first;
//如果元素为空,只需要考虑链表元素为空
if(element==null){
for(int i=0;i<size;i++){
if(node.element==null)return i;
node=node.next;
}
/*while(node!=null){
...
node=node.next;
}*/
}else{//元素不为空则调用equals方法判断
for(int i=0;i<size;i++) {
if (element.equals(node.element)) return i;
node=node.next;
}
}
//未找到则返回常量-1
return ELEMENT_NOT_FOUND;
}
@Override
public void clear() {
//将头节点设为null,则头节点指向Node对象将被销毁,
// 导致存储地址被销毁,直至所有Node对象被销毁
first=null;
size=0;
}
@Override
public void add(int index, E element) {
//考虑边界
if(index==0){
//新建节点指向first节点,并更新first节点
first= new Node<E>(element, first);
}else{
//获取插入节点的先前节点
Node<E> prev=node(index-1);
//新建节点指向prev的next节点,prev指向新节点
prev.next=new Node<E>(element,prev.next);
}
size++;
}
@Override
public E remove(int index) {
rangeCheck(index);
E oldElement= first.element;
//判断是否是第一个节点
if(index==0){
first=first.next;
}else{
//获取前驱节点
Node<E> prev=node(index-1);
//保存被移除节点数据
oldElement=prev.next.element;
//跳过next指向next.next
prev.next=prev.next.next;
}
size--;
return oldElement;
}
@Override
public E get(int index) {
Node<E> node=node(index);
return node.element;
}
@Override
public E set(int index, E element) {
Node<E> node=node(index);
E oldElement=node.element;
node.element=element;
return oldElement;
}
//获取索引为index的节点
private Node<E> node(int index){
rangeCheck(index);
Node<E> node=this.first;
for(int i=0;i<index;i++)
node=node.next;
return node;
}
@Override
public String toString() {
StringBuilder sb=new StringBuilder("[");
Node<E> node=first;
while(node!=null){
//判断是否是最后一个节点
if(node.next!=null)
sb.append(node.element).append(" ,");
else
sb.append(node.element);
node=node.next;
}
sb.append("]");
return sb.toString();
}
}
测试类
public class LinkedListTest {
public static void main(String[] args) {
LinkedList<String> linkedList=new LinkedList<>();
linkedList.add("one");
linkedList.add("two");
linkedList.add(0,"three");
linkedList.remove(1);
//[three ,two]
System.out.println(linkedList);
System.out.println(linkedList.get(1));
linkedList.remove(0);
System.out.println(linkedList);
linkedList.add(0,"hello");
System.out.println(linkedList);
}
}
使用虚拟头节点的链表
有时候为了让代码更加精简,统一所有节点的处理逻辑,可以在最前面增加一个虚拟的头结点(不存储数据)。因此使用虚拟头节点需要对以上代码做出一些修改,改动的方法为:
方法 | 功能 |
---|---|
public LinkedList() | 构造方法中需要创建虚拟头节点 |
public int indexOf(E element) | 修改初始节点位置 |
public void clear() | 修改first.next=null |
public void add(int index, E element) | 统一节点处理逻辑 |
public E remove(int index) | 统一节点处理逻辑 |
private Node node(int index) | 修改初始节点位置 |
public class LinkedList<E> extends AbstractList<E> {
//定义普通内部类Node
private class Node<E>{
private E element;
private Node<E> next;
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
private Node<E> first;//虚拟头节点
public LinkedList(){//构造方法中需要创建虚拟头节点
first=new Node<E>(null,null);
}
@Override
public int indexOf(E element) {
Node<E> node=first.next;
//如果元素为空,只需要考虑链表元素为空
if(element==null){
for(int i=0;i<size;i++){
if(node.element==null)return i;
node=node.next;
}
/*while(node!=null){
...
node=node.next;
}*/
}else{//元素不为空则调用equals方法判断
for(int i=0;i<size;i++) {
if (element.equals(node.element)) return i;
node=node.next;
}
}
//未找到则返回常量-1
return ELEMENT_NOT_FOUND;
}
@Override
public void clear() {
//将头节点设为null,则头节点指向Node对象将被销毁,
// 导致存储地址被销毁,直至所有Node对象被销毁
first.next=null;
size=0;
}
@Override
public void add(int index, E element) {
//获取插入节点的先前节点
Node<E> prev=(index==0)?first:node(index-1);
//新建节点指向prev的next节点,prev指向新节点
prev.next=new Node<E>(element,prev.next);
size++;
}
@Override
public E remove(int index) {
rangeCheck(index);
//获取前驱节点,如果索引为0则使用虚拟头节点
Node<E> prev=index==0?first:node(index-1);
//保存被移除节点数据
E oldElement=prev.next.element;
//跳过next指向next.next
prev.next=prev.next.next;
size--;
return oldElement;
}
@Override
public E get(int index) {
Node<E> node=node(index);
return node.element;
}
@Override
public E set(int index, E element) {
Node<E> node=node(index);
E oldElement=node.element;
node.element=element;
return oldElement;
}
//获取索引为index的节点
private Node<E> node(int index){
rangeCheck(index);
Node<E> node=this.first.next;
for(int i=0;i<index;i++)
node=node.next;
return node;
}
@Override
public String toString() {
StringBuilder sb=new StringBuilder("[");
Node<E> node=first.next;
while(node!=null){
//判断是否是最后一个节点
if(node.next!=null)
sb.append(node.element).append(" ,");
else
sb.append(node.element);
node=node.next;
}
sb.append("]");
return sb.toString();
}
}