Java小白翻身-手写LinkedList2

简介: Java小白翻身-手写LinkedList

编译器认为你这是把一个相同的方法写了两遍,所以给你报错了,再强调一遍,这可不是方法重载哦!

那么,如何修改add方法,才可以不报错呢?

很简单,让它的返回值和AbstractList里面的add方法一致就可以了。

public boolean add(Object object){
  //将数据用节点类包装好,这样才能实现下一个数据的指向
  Node data = new Node(object);
  //先判断是否是第一个节点
  if(this.firstNode == null){
    this.firstNode = data;
    this.currentNode = data;
  }else{
    //不是第一个节点,就指向当前节点的下一个节点,即currentNode.next
    this.currentNode.next = data;
    //因为已经指向下一个了,所以当前节点也要移动过来
    this.currentNode = data;
  }
  return true;
}


这样就变成方法重写了。

我们来看下AbstractList中的原生add方法是怎么写的:

    public boolean add(E e) {
        add(size(), e);
        return true;
    }

我们可以发现,add方法接收一个参数e,然后立刻又调用了另一个add方法。很明显,这是方法重载,因为他的参数列表不同,接收两个参数。其中第一个参数size(),是一个方法的调用,这边得到的肯定是size方法的返回值。我们盲猜这个方法的作用是获取当前链表的长度,意思就是add方法增加的数据,直接就拼接到链表的末尾了。


到底是大佬写的代码啊,想的就是全面,而我们写的add方法,就没考虑那么多,直接添加到末尾了。


其实不一定的,万一我们要添加到链表的中间位置呢,是不是完全有可能呢?


那么,我们再来欣赏一下这个重载的add方法吧:


59.png


额,直接抛异常了?

再看看size:

public abstract int size();

直接就是抽象方法,而且,在他的子类AbstractSequentialList中也没有实现这个size方法,那么只能由继承AbstractSequentialList的LinkedList去实现啦。

60.png


至于那个add方法,直接抛了异常,意思大概就是我们必须去重写它了。虽然在AbstractSequentialList中,重写了这个add,但是我们不用它,因为它的设计太复杂了,不适合我们新手学习。


重写size方法

先挑软柿子捏,size方法无非就是获取链表的长度,来,搞起!


1、size思路

2、代码

我们可以抄袭,哦不,借鉴 java.util 包里面LinkedList中size的做法,就是直接设置一个int属性,每次add的时候就+1,remove的时候就-1。


61.png


我去,这个transient关键字是啥哦?


解释:


java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。


不过呢,现在我们很少用到java里面的序列化了,基本都是json传输,或者xml传输。对于一些敏感的属性,我们可以通过注解的方式过滤掉,所以这个知识点我们简单识记一下即可。


显示增加属性:

private int size = 0; //链表中的元素个数,初始化为0

然后是size方法

@Override
public int size() {
    return this.size;
}


没错,就这么简单。

重载add方法,这个才是最通用的。


public void add(int index, E element) {
        try {
            listIterator(index).add(element);
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }

原来,父类AbstractSequentialList并没有亏待咱,已经写好了这个重载的add方法。不过呢,它的实现比较复杂,而且是针对双向链表的。我们的LinkedList是单链表,就不整那么复杂了,但是,我们可以参考下大佬的写法。

@Override
public void add(int index, Object element) {
    boolean isOutSize = checkIndex(index);
    if(isOutSize){
        throw new IndexOutOfBoundsException("Index: "+index + "已经超出范围了,目前最大容量为" + size());
    }
}
/**
 * 检查下标是否超限
 * @param index
 * @return
 */
private boolean checkIndex(int index) {
    return index > size;
}

上面有个超纲的内容,就是异常抛出,IndexOutOfBoundsException是范围超出异常,你现在就理解成程序手动报错就行了。

测试一下,先生成size的get,set方法。

public int getSize() {
    return size;
}
public void setSize(int size) {
    this.size = size;
}

然后,原来的add方法加上size的自增代码:

62.png


public static void main(String[] args) {
    LinkedList list = new LinkedList();
    list.add("苹果");
    list.add("梨子");
    System.out.println(list.getSize());
}


63.png


就报错了,符合我们的预期。

@Override
public void add(int index, Object element) {
  boolean isOutSize = checkIndex(index);
  if(isOutSize){
    throw new IndexOutOfBoundsException("Index: "+index + "已经超出范围了,目前最大容量为" + size());
  }
  int loop = 0;
  Node node = firstNode; //从第一个元素开始
  while(node != null) {
    loop++;
    if(loop == index){
      //如果已经是最后一个,则直接拼接
      if(loop == size()){
        Node newNode = new Node(element);
        node.next = newNode;
        //跳出循环
        break;
      }
      //1.先保存当前节点的next,防止断尾
      Node temp = new Node(node.next != null ? node.next.data:null);
      temp.next = node.next != null ? node.next.next : null;
      //2.之前断掉的部分接在新节点后面
      Node newNode = new Node(element);
      newNode.next = temp;
      //3.新的节点接在node后面
      node.next = newNode;
      //跳出循环
      break;
    }else{
      node = node.next;
    }
  }
}
/**
 * 检查下标是否超限
 * @param index
 * @return
 */
private boolean checkIndex(int index) {
  return index > size || index <= 0;
}


我们先用display方法来做测试。

LinkedList list = new LinkedList();
list.add("苹果");
list.add("梨子");
list.add(1,"柚子");
list.display();


64.png


for (Object o : list) {
    System.out.println(o);
}

咋就空指针了呢?

这边还不好调试,只能去看反编译的代码:

65.png


istIterator,好眼熟啊,我们好像重写了对不?

66.png


对哦,当时我们嫌麻烦,就写了一个假的实现,直接就返回null了,所以肯定报错啊,哈哈。

正好复习一下之前的知识点,我们再来new一个匿名实现类。

LinkedList list = new LinkedList();
list.add("苹果");
list.add("梨子");
list.add(2,"柚子");
list.forEachRemaining(new Consumer() {
    @Override
    public void accept(Object o) {
        System.out.println(o);
    }
});


逼格瞬间就高了。

67.png

假如知道了下标,就该拿到对应的元素

//取出某一个元素
public Object get(int index){
  if(checkIndex(index+1)){
    return null;
  }
  int i = 0;
  Object o = null;
  while((o = this.next())!= null){
    if(i++ == index){
      nodeForEach = null;//重置index
      return o;
    }
  }
  nodeForEach = null;//重置index
  return o;
}

思路:先判断一下下标是否超限,然后去遍历所有节点,如果找到了对应的index,直接把value返回即可。

注意,不管有没有取出元素后,都要重置nodeForEach,不然遍历的时候会出问题。


相关文章
|
Java 程序员 Windows
【Java知识点详解 10】为何要配置环境变量,35岁老年程序员的绝地翻身之路
【Java知识点详解 10】为何要配置环境变量,35岁老年程序员的绝地翻身之路
|
Java 数据安全/隐私保护
java小白翻身-异常处理03: 自定义异常
java小白翻身-异常处理03: 自定义异常
119 0
|
存储 Java
java小白翻身-异常处理02: 常见异常
归纳一些Java开发中常见的一些异常。
127 0
|
算法 Java 测试技术
java小白翻身-手写HashMap
这一节课,我们来手写一个简单的HashMap,所谓HashMap,就是一个映射表。
104 0
|
存储 Java 编译器
Java小白翻身-手写LinkedList1
Java小白翻身-手写LinkedList
107 0
|
安全 Java BI
Java小白翻身-Excel教程
Java小白翻身-Excel教程
140 0
|
Java API
Java小白翻身 - webservice教程2
来一个HelloWorld,SpringBoot发布WebService可简单啦。
286 0
|
IDE Java 开发工具
Java小白翻身教程-链表结构与编译大法(4)
tools.jar是刚打出来的工具包,现在把它导入项目。
123 0
Java小白翻身教程-链表结构与编译大法(3)
D盘下面的tool文件夹已经有三个工具类了(其实是两个,CustNode是为了TuziLinkedList服务的),我们这一节来进行打包,这样的好处就是不用每次编译都把tool里面的类也带上了。
113 0