1. 首页
  2. Java基础

048-四十八、Java之手写LinkedList(中)

public T get(int index)

得到指定位置的节点。

由于今天要写add(int index,T t)方法,索引会把内部类中的递归的get(int index)改造成获取节点,不直接获取元素,外部类的get方法也会稍加改动。

外部类改造Node<T> node = first.get(index,0);//这里改造

内部类改造


/** * 得到指定位置的节点, * @param index * @return */ public T get(int index) { //下标越界提醒 arrayIndexOutOfBoundsException(index); /** * 以上条件都不满足,那么就开始递归查询 * 为什么大家都说LinkedList的get效率低呢? * 主要是因为get的时候需要逐个遍历来匹配获取数据,这样效率就低很多 了。 * ArrayList是直接操作数组的,get也是直接在数组里面根据索引获取的。 * * 因为linkedList是没有index属性的,所以需要一个临时变量,那么直接传入一个0进入方法即可 * 因为需要逐个递归需要和索引比配上才能找到对应的元素 */ /** * 根据index获取对应的节点 * 这里改造 */ Node<T> node = this.first.get(index,0); return node.data; }

抽取了下标越界方法


/** * 下标越界异常 * @param index */ private void arrayIndexOutOfBoundsException(int index){ /** * size==0表示链表中没有数据,直接抛出异常 * index>=size或者index<0,表示index索引已经越界,直接抛出异常 */ if (size == 0 || index >= size || index < 0) { throw new ArrayIndexOutOfBoundsException("下标越界!!!"); } }

public void add(int index,T t)

向链表指定位置添加一个新节点,该节点中的数据是参数element指定的对象


/** * 向链表指定位置添加一个新节点,该节点中的数据是参数element指定的对象 * @param index * @param t */ public void add(int index,T t) { //下标越界提醒 arrayIndexOutOfBoundsException(index); /** * 1、获取要插入的节点和节点位置。 */ Node<T> oldNode = first.get(index,0); /** * 每次在添加的时候就创建一个节点 */ final Node<T> newNode = new Node<>(t); /** * 如果oldNode.prev为null就表示为first节点 * 反过来就是不等于null的话那么就需要将新节点(newNode.prev)的引用值等于老节点oldNode.prev * =oldNode.prev */ if (oldNode.prev != null) { newNode.prev = oldNode.prev; //并且需要将newNode节点的上级(prev)节点的next值等于newNode newNode.prev.next = newNode; } //然后改变oldNode.prev=newNode oldNode.prev = newNode; //将oldNode节点引用存放到新插入的节点next下 newNode.next = oldNode; //元素总量增加1 this.size++; }

public void addLast(T t)

向链表表尾添加一个新节点,该节点中的数据是参数element指定的对象
这个就简单了,外部类直接就有最有一个节点的引用,直接互换就可以了。


/** * 向链表表尾添加一个新节点,该节点中的数据是参数 t 指定的对象 * @param t */ public void addLast(T t) { /** * 这个就简单了,外部类直接就有最有一个节点的引用,直接互换就可以了。 */ Node<T> oldLast = this.last; /** * 每次在添加的时候就创建一个节点 */ final Node<T> newNode = new Node<>(t); //直接互换位置即可 this.last = newNode; this.last.prev = oldLast; oldLast.next = this.last; this.size++; }

这样看到了这个linkedlist的插入效率高多了吧。

public Object removeFirst()

删除第一个节点并返回这个节点中的对象


/** * 删除第一个节点并返回这个节点中的对象 * @return */ public T removeFirst() { /** * 如果first直为空,表示没有数据可删除,直接返回null */ if (this.first == null) { return null; } Node<T> oldFirst = this.first; /** * 直接将第二个节点替换成first节点 * 然后将first节点的prev值设置为null就好了 * 不过首先需要判断一下first.next是否为null * 如果为null就表示只有一个元素 */ if (this.first.next != null) { this.first = this.first.next; this.first.prev = null; } this.size--; return oldFirst.data; }

删除是不是效率也高吧!

public Object removeLast()

删除最后一个节点并返回这个节点中的对象


/** * 删除最后一个节点并返回这个节点中的对象 * @return */ public T removeLast() { /** * 如果last直为null,表示没有数据可删除,直接返回null */ if (this.last == null) { return null; } Node<T> oldLast = this.last; /** * 直接将倒数第二个节点替换成last节点 * 然后将last节点的next值设置为null就好了 * 首先也的判断一下last的上一个节点是否为null * 如果是那么就表示只有数据 */ this.last = this.last.prev; this.last.next = null; this.size--; return oldLast.data; } public T remove(int index) 删除指定位置的节点 /** * 删除指定位置的节点 * @param index * @return */ public T remove(int index) { //下标越界提醒 arrayIndexOutOfBoundsException(index); /** * 获取要删除的节点保存到临时变量中 */ Node<T> removeNode = this.first.get(index,0); /** * 删除中间节点 * 如果节点prev不为null表示不是first节点 * 如果节点next不为null表示不是last节点 * 如果找到了直接互换perv和next即可 */ if (removeNode.prev != null && removeNode.next != null) { removeNode.prev.next = removeNode.next; removeNode.next.prev = removeNode.prev; } /** * 删除first节点 * 表示为first节点 * 直接将第二个节点移位到first节点 * 并且将first.prev=null即可 */ else if (removeNode.prev == null && removeNode.next != null) { this.first = removeNode.next; this.first.prev = null; } /** * 删除last节点 * 直接将倒数第二个节点next=null即可 */ else if (removeNode.prev != null) { this.last = removeNode.prev; this.last.next = null; } this.size--; return removeNode.data; } 自定义MyLinkedList源码 /** * @author dcc */ public class MyLinkedList<T> { /** * 链表元素的长度 */ private int size; /** * 链表的第一个节点 */ private Node<T> first; /** * 链表的最后一个节点 */ private Node<T> last; /** *向链表末尾添加一个新节点,该节点中的数据是参数t指定的对象 * @param t * @return */ public boolean add(T t){ /** * 每次在添加的时候就创建一个节点 */ final Node<T> node = new Node<>(t); /** * 当第一次添加的时候就将节点赋值给first,表示第一个节点的引用 */ if(this.first == null){ //第一个节点 this.first = node; //第一次第一个节点和最后一个节点相同 this.last = this.first; }else { /** * 因为每次add的时候都会add到最后一个节点上, * 那么这个时候last需要赋值给previousNode * 这个时候previousNode节点就变成倒数第二个节点了 * 也就是说新的node节点变成last节点了 * 并且previousNode变成了last节点的上一个节点了 * 这样就证明LinkedList添加操作效率就比ArrayList操作效率高多了。 */ Node<T> previousNode = this.last; this.last = node; this.last.prev = previousNode; previousNode.next = this.last; } this.size++;//表示元素的总个数 return true; } /** * 得到指定位置的节点, * @param index * @return */ public T get(int index) { //下标越界提醒 arrayIndexOutOfBoundsException(index); /** * 以上条件都不满足,那么就开始递归查询 * 为什么大家都说LinkedList的get效率低呢? * 主要是因为get的时候需要逐个遍历来匹配获取数据,这样效率就低很多 了。 * ArrayList是直接操作数组的,get也是直接在数组里面根据索引获取的。 * * 因为linkedList是没有index属性的,所以需要一个临时变量,那么直接传入一个0进入方法即可 * 因为需要逐个递归需要和索引比配上才能找到对应的元素 */ /** * 根据index获取对应的节点 * 这里改造 */ Node<T> node = this.first.get(index,0); return node.data; } /** * 向链表表头添加一个新节点,该节点中的数据是参数element指定的对象 * @param t */ public void addFirist(T t) { Node<T> newNode = new Node<>(t); /** * 第一步需要判断first是否为null,为null就添加 * 在这里需要将oldFirst节点也就是老的first节点,添加到新的first节点上 */ if (this.first != null) { /** * 首先将first的引用保存在一个临时变量oldFirst中 */ Node oldFirst = this.first; /** * 将这个节点存放在first节点上 */ this.first = newNode; this.first.next = oldFirst; /** * 然后将first的引用赋值给oldFirst.prev就好了 */ oldFirst.prev = this.first; if (this.last == null) { this.last = this.first; } } else {//表示为第一次添加 //第一个节点 this.first = newNode; //第一次第一个节点和最后一个节点相同 this.last = this.first; } this.size++; } /** * 向链表指定位置添加一个新节点,该节点中的数据是参数element指定的对象 * @param index * @param t */ public void add(int index,T t) { //下标越界提醒 arrayIndexOutOfBoundsException(index); /** * 1、获取要插入的节点和节点位置。 */ Node<T> oldNode = first.get(index,0); /** * 每次在添加的时候就创建一个节点 */ final Node<T> newNode = new Node<>(t); /** * 如果oldNode.prev为null就表示为first节点 * 反过来就是不等于null的话那么就需要将新节点(newNode.prev)的引用值等于老节点oldNode.prev * =oldNode.prev */ if (oldNode.prev != null) { newNode.prev = oldNode.prev; //并且需要将newNode节点的上级(prev)节点的next值等于newNode newNode.prev.next = newNode; } //然后改变oldNode.prev=newNode oldNode.prev = newNode; //将oldNode节点引用存放到新插入的节点next下 newNode.next = oldNode; //元素总量增加1 this.size++; } /** * 向链表表尾添加一个新节点,该节点中的数据是参数 t 指定的对象 * @param t */ public void addLast(T t) { /** * 这个就简单了,外部类直接就有最有一个节点的引用,直接互换就可以了。 */ Node<T> oldLast = this.last; /** * 每次在添加的时候就创建一个节点 */ final Node<T> newNode = new Node<>(t); //直接互换位置即可 this.last = newNode; this.last.prev = oldLast; oldLast.next = this.last; this.size++; } /** * 删除第一个节点并返回这个节点中的对象 * @return */ public T removeFirst() { /** * 如果first直为空,表示没有数据可删除,直接返回null */ if (this.first == null) { return null; } Node<T> oldFirst = this.first; /** * 直接将第二个节点替换成first节点 * 然后将first节点的prev值设置为null就好了 * 不过首先需要判断一下first.next是否为null * 如果为null就表示只有一个元素 */ if (this.first.next != null) { this.first = this.first.next; this.first.prev = null; } this.size--; return oldFirst.data; } /** * 删除最后一个节点并返回这个节点中的对象 * @return */ public T removeLast() { /** * 如果last直为null,表示没有数据可删除,直接返回null */ if (this.last == null) { return null; } Node<T> oldLast = this.last; /** * 直接将倒数第二个节点替换成last节点 * 然后将last节点的next值设置为null就好了 * 首先也的判断一下last的上一个节点是否为null * 如果是那么就表示只有数据 */ this.last = this.last.prev; this.last.next = null; this.size--; return oldLast.data; } /** * 删除指定位置的节点 * @param index * @return */ public T remove(int index) { //下标越界提醒 arrayIndexOutOfBoundsException(index); /** * 获取要删除的节点保存到临时变量中 */ Node<T> removeNode = this.first.get(index,0); /** * 删除中间节点 * 如果节点prev不为null表示不是first节点 * 如果节点next不为null表示不是last节点 * 如果找到了直接互换perv和next即可 */ if (removeNode.prev != null && removeNode.next != null) { removeNode.prev.next = removeNode.next; removeNode.next.prev = removeNode.prev; } /** * 删除first节点 * 表示为first节点 * 直接将第二个节点移位到first节点 * 并且将first.prev=null即可 */ else if (removeNode.prev == null && removeNode.next != null) { this.first = removeNode.next; this.first.prev = null; } /** * 删除last节点 * 直接将倒数第二个节点next=null即可 */ else if (removeNode.prev != null) { this.last = removeNode.prev; this.last.next = null; } this.size--; return removeNode.data; } /** * 下标越界异常 * @param index */ private void arrayIndexOutOfBoundsException(int index){ /** * size==0表示链表中没有数据,直接抛出异常 * index>=size或者index<0,表示index索引已经越界,直接抛出异常 */ if (size == 0 || index >= size || index < 0) { throw new ArrayIndexOutOfBoundsException("下标越界!!!"); } } /** * 获取数组长度 * @return */ public int size(){ return this.size; } /** * 这个内部类就是每次添加的节点 * @param */ private class Node<T>{ /** * 要添加的数据 */ private T data; /** * 下个节点的引用 */ private Node<T> next; /** * 上一个节点引用 */ private Node<T> prev; /** * 每次构造的时候index加1 * @param t */ private Node(T t) { this.data = t; } /** * 获取index索引的节点 * @param index * @return */ private Node<T> get(int index,int tempIndex) { /** * 匹配就直接返回了 */ if (index == tempIndex) { return this; } /** * 如果传入索引和临时索引不匹配将递归到下一个节点在进行匹配 * 以此类推 */ return this.next.get(index,++tempIndex); } } }

写完了如果写得有什么问题,希望读者能够给小编留言,也可以点击[此处扫下面二维码关注微信公众号](https://www.ycbbs.vip/?p=28 "此处扫下面二维码关注微信公众号")

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「方志朋」,公众号后台回复「666」 免费领取我精心整理的进阶资源教程
  4. JS中文网,Javascriptc中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,是给开发者用的 Hacker News,技术文章由为你筛选出最优质的干货,其中包括:Android、iOS、前端、后端等方面的内容。目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。

    本文著作权归作者所有,如若转载,请注明出处

    转载请注明:文章转载自「 Java极客技术学习 」https://www.javajike.com

    标题:048-四十八、Java之手写LinkedList(中)

    链接:https://www.javajike.com/article/1355.html

« 049-四十九、Java之手写LinkedList(下)
047-四十七、Java之手写LinkedList(上)»

相关推荐

QR code