代码已实现的方法:
方法名 | 作用 | 备注 |
---|---|---|
clear() | 初始化链表&清空链表 | 两个作用 |
size() | 取链表容量 | |
isEmpty() | 判断链表是否为空 | |
add() | 往链表中插入一个值 | 多个构造方法,含私有方法 |
set() | 修改链表指定位置元素的值 | 同上 |
get() | 取索引位置元素值 | 同上 |
remove() | 删除索引元素 | 同上 |
iterator() | 取链表迭代器 |
LinkedList相关知识科普:
-
LinkedList可以动态添加、删除、修改、查找容器中的元素。因此要使得我们的LinkedList具有最基本的自理能力,必须要实现以上增删改查方法。
-
在java容器中,其底层实现方式为双端链表。因此其初始(容器为空)结构为:首节点与尾结点相互连接,首节点下一个节点为尾结点,尾结点上一个节点为首节点。
-
LinkedList中的迭代器,被用于快速遍历LinkedList结构的元素,其优点在于LinkedList内部的结构对调用者是透明的,无需关心结构,便可以拿到数据。省去了开发人员自己探索LinkedList的结构过程。
-
迭代器内部结构说明:除了以下三个方法,还有三个属性:
- 用于记录当前扫描到的节点:(Node)currentNode;
- 用于记录从双端链表生成以来对链表长短修改的次数:(Int)expectedModCount,值得说明的是,这个检测是用来防止迭代器迭代过程中程序对链表长短的修改,具体实现方法是这样的,在LinkedList中有个modCount,每当用户增加或删除节点操作时,modCount都会+1,用户取用迭代器时,会为迭代器的expectedModCount赋值为modCount,使用迭代器next取值时,便会检查expectedModeCount与modCount是否相同,不相同时,说明链表结构被修改,接下来读出的值有可能是已经读过的,导致取值错误,因此此时会抛出ConcurrentModificationException异常;
- 用于记录当前状态是否可以执行remove方法的(boolean)okToRemove, 此值默认为false, 会在next方法执行过程中更改为true,表示当前节点有prev节点,可以删除prev节点。
-
迭代器主要实现三个方法:
方法名 | 作用 | 备注 |
---|---|---|
hasNext | 检测当前节点是否为尾结点 | 实现方法:通过判断currentNode是否是尾结点 |
next | 读取返回当前节点的值并指向下一个节点 | 需要先检验:1.hasNext检验;2.迭代器expectedModCount与LinkedList中modCount值是否一致 |
remove | 从双端队列中删除当前节点 | 需要先检验:1同next方法第二条检测.;2. okToRemove是否为true; |
收获:
- 内部类
- 内部类可以分为静态与非静态,其定义和创建方法如下:
- 通过使用静态类,可以更方便类的创建。
- 泛型运用
- 定义节点Node<T>,也可以写成Node<AnyType>,此写法表示该结构内部可以容纳任何对象,从而起到容器支持任何对象的泛化能力。
- 我们对其抽象时,可以认为在含有泛型的对象或者方法中,其可以处理任意对象即可。
- 类限定词运用
- 在本实例中,将需要提供给用户的接口使用public定义,而实现public方法的工具类使用private定义,保证最原始的实现方法不对外部公开。
- 方法调用设计
- 这里我们关注方法的返回值,在增删改查功能方法中,增加元素时,不需要返回值,其他三类操作都需要返回对应的数据,如删除数据时,应将被删除的数据返回,修改数据时,应将被修改的数据返回。
- 缩短链表扫描时间方法
- 根据索引寻找节点时,当被寻找元素索引在[0,size()/2]区间,则从左向右扫描,否则从右向左扫描。
- 类中方法设计遵守的原则
- 对外提供的接口使用public限定,内部实现public方法的辅助方法使用private限定保证对外隐藏。
- 由private实现同种功能的一次实现,其他衍生出的重载方法可在private的方法之上计算参数,并传入private完成指定功能,例如:add方法可以按照索引插入,也可以在某个元素之前插入,因此底层private需要定义为在某位置插入某个元素,此时按照索引插入的方法可以自行计算好需要插入的元素位置,然后调用在某个元素之前插入,再由private执行具体的插入方法。
- 内部类如何调用其父类方法
- 内部类中调用父类的方法时IDE会报错找不到该方法,假如外部类为MyLinkedLIst,需要调用的外部类中的方法名remove(Node<T>),则在内部类的方法中使用:MyLinkedList.this.remove(xx);
- 并发安全检测
- 在LinkedList中,使用modCount来记录从链表创建开始,对链表长度修改的次数(增长或缩短都会对modCount加一),当用户调用获取LinkedLIst迭代器的后,会将modCount的值赋予迭代器中的expectedModCount,每次迭代器进行next或者remove操作时都会检测LinkedList中的modCount和自己expectedModCount的值是否一致,一致则执行相应操作,此举目的在于,防止迭代器取值过程中,外部对该链表长度进行修改从而使得迭代器取值错误。
- 迭代器实现原则
- 迭代器的意义在于,使得用户不再关心数据链的结构从而快速从某结构中获取数据,节省用户自行遍历时对数据结构的探索。
- 迭代器需要实现三个功能:1.hasNext:用于检测当前节点是否为尾结点;2. next:用于取出当前节点的值并将节点指针指向下一个节点;3.remove:用于删除上一个节点。
- 类的属性及方法属性初值可否为空问题
- jvm会对类的属性赋初值,但是不会对类方法中的局部变量赋初值,因此若局部变量定义时,未赋初值则IDE会报错。
思考
- 关于代码结构:
MyLinkedList设计思想中,与经典MVC结构类似,或者与视图计算层分离思想类似(通过设计缓冲队列,视图层月计算层分别面向缓冲队列数据进行展示和计算),将private作为最底层的真正的逻辑实现,通过多种构造方法相互计算之间的参数,实现一个private方法的多种条件计算。
代码附录
- 代码区:
- 测试代码:
package DatastructureImplement;
import java.util.Iterator;
public class Playground {
public static <E> void main(String[] args) {
/**
* test:
* add()
* get()
*/
MyLinkedList<Integer> mld = new MyLinkedList<Integer>();
for(int i = 0;i < 10;i++){
mld.add(i);
}
for(int i = 9;i>=0;i--){
System.out.println(mld.get(i));
}
/**
* test:
* set()
* remove()
*/
System.out.println("-------------");
System.out.println(mld.set(9, 99)); //9
System.out.println(mld.remove(9)); //99
System.out.println(mld.toString());
/**
* iterator test
*/
Iterator<Integer> iterator = mld.iterator();
System.out.println("=================");
while(iterator.hasNext()){
System.out.println(iterator.next());
iterator.remove();
}
System.out.println("-=-=-=-=-=-=-=-=-=");
System.out.println(mld.toString());
}
}
-
- MyLinkedList实现代码:
package DatastructureImplement;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class MyLinkedList<T> implements Iterable<T>{
private static class Node<T>{
public Node(T d,Node<T> p,Node<T> n) {
// TODO Auto-generated constructor stub
data = d;
prev = p;
next = n;
}
public T data;
public Node<T> prev;
public Node<T> next;
}
public MyLinkedList() {
// TODO Auto-generated constructor stub
clear();
}
public void clear(){doClear();}
private void doClear(){
beginMarker = new Node<T>(null, null, endMarker);
endMarker = new Node<T>(null, beginMarker, null);
theSize = 0;
modCount++;
}
/**
* clear() -x
* size() -x
* isEmpty()-x
* add() -> addBefore -x
* set() -x
* get() -x
* remove() -x
*
* Utils method:
* getNode() -x
* getNode(int idx)
* getNode(int idx, int lower, int upper)
* remove() -x
* remove(int idx)
* remove(Node<T> node)
*/
public int size(){return theSize;}
public boolean isEmpty(){return size()==0;}
public T remove(int idx){
return remove(getNode(idx));
}
public T remove(Node<T> node){
node.next.prev = node.prev;
node.prev.next = node.next;
modCount++;
theSize--;
return node.data;
}
public T get(int idx){
return getNode(idx).data;
}
public T set(int idx, T newVal){
Node<T> node = getNode(idx);
T oldVal = node.data;
node.data = newVal;
return oldVal;
}
public void add(T data){
addBefore(getNode(size()),data);
}
public void addBefore(Node<T> node,T data){
Node<T> newNode = new Node<T>(data, node.prev, node);
node.prev.next = newNode;
node.prev = newNode;
theSize++;
modCount++;
}
private Node<T> getNode(int idx){
return getNode(idx,0,size());
}
private Node<T> getNode(int idx, int lower, int upper){
Node<T> retNode; //成员变量默认有初值,而成员方法中的局部变量没有初值。因此如果不赋值直接返回ide会报错。
// idx regular check
if(idx < lower || idx > upper) throw new IndexOutOfBoundsException();
if(idx < size()/2){
retNode = beginMarker.next;
for(int i = lower; i < idx;i++){
retNode = retNode.next;
}
}else{
retNode = endMarker;
for(int i = size();i > idx;i--){
retNode = retNode.prev;
}
}
return retNode;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
for(int i = 0;i<theSize;i++){
if(i != 0){
sb.append(",");
}
sb.append(get(i)+"");
}
return sb.toString();
}
private int theSize;
//记录对链表所作的改变次数,用于与迭代器中modCount匹配,当失配时抛出ConcurrentModificationException
//其想法在于,当一个迭代器被创建时,它将存储集合的modCount, 使用modCount来判断是否并发安全。
private int modCount = 0;
private Node<T> beginMarker;
private Node<T> endMarker;
@Override
public Iterator<T> iterator() {
return new MyLinkedListIterator();
}
private class MyLinkedListIterator implements Iterator<T>{
private Node<T> currentNode = beginMarker.next;
private int expectedModCount = modCount;
private boolean okToRemove = false;
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return currentNode != endMarker;
}
@Override
public T next() {
// TODO Auto-generated method stub
modCountCheck();
if(!hasNext()) throw new NoSuchElementException();
T nextItem = currentNode.data;
currentNode = currentNode.next;
okToRemove = true;
return nextItem;
}
@Override
public void remove() {
// TODO Auto-generated method stub
modCountCheck();
if(!okToRemove) throw new IllegalStateException();
// remove(currentNode.prev);
MyLinkedList.this.remove(currentNode.prev); //this表示一个实例
okToRemove = false;
expectedModCount++;
}
private void modCountCheck(){
if(expectedModCount != modCount) throw new ConcurrentModificationException();
}
}
}