• 自定义(双链表)集合类


    包结构

    MyList 接口 

     1 package day2_19.inter;
     2 
     3 /**
     4  * 将arryList 和 linkedList 共性的方法进行抽取 --->保证体系的完整性
     5  *
     6  * @Author Tianhao
     7  * @create 2021-02-19-17:26
     8  */
     9 
    10 public interface MyList<E>{
    11 
    12 
    13     public int size();
    14 
    15     public boolean isEmpty();
    16     
    17     public boolean contains(Object o);
    18 
    19     public boolean add(E e);
    20 
    21     public void add(int index, E element);
    22 
    23     public E remove(int index);
    24 
    25     public E get(int index);
    26     
    27     public E set(int index, E element);
    28     
    29     public int indexOf(Object o);
    30 
    31     public void clear();
    32 
    33     String toString();
    34 
    35 }

    MyAbstractList 抽象类
     1 package day2_19.abstractClass;
     2 
     3 import day2_19.inter.MyList;
     4 
     5 /**
     6  *
     7  * 用于实现arryList 和 linkedList中那些相同的方法
     8  *
     9  * @Author Tianhao
    10  * @create 2021-02-19-17:46
    11  */
    12 
    13 public abstract class MyAbstractList<E> implements MyList<E> {
    14     protected int size;
    15 
    16     /**
    17      * 获取元素个数
    18      * @return 元素个数
    19      */
    20     @Override
    21     public int size() {
    22         return size;
    23     }
    24 
    25     /**
    26      * 判断集合是否为空
    27      * @return
    28      */
    29     @Override
    30     public boolean isEmpty() {
    31         return size == 0;
    32     }
    33 
    34     /**
    35      * 判断集合是否包含指定元素
    36      * indexOf(Object o):寻找对应的元素,如果找到了返回元素的索引,如果没有找到返回 -1
    37      * @param o
    38      * @return
    39      */
    40     @Override
    41     public boolean contains(Object o) {
    42         return indexOf(o) != -1;
    43     }
    44 
    45 
    46     /**
    47      * 在集合的最后添加一个元素,也就是在索引为size处添加一个元素
    48      * @param e
    49      * @return
    50      */
    51     @Override
    52     public boolean add(E e) {
    53         add(size,e);
    54         return true;
    55     }
    56 }
    MyLinkedList 类
      1 package day2_19.linked;
      2 
      3 import day2_19.abstractClass.MyAbstractList;
      4 
      5 /**
      6  *
      7  * 自定义(单链表)集合类
      8  *
      9  * @Author Tianhao
     10  * @create 2021-02-19-18:01
     11  */
     12 public class MyLinkedList<E> extends MyAbstractList<E> {
     13 
     14     private Node<E> first;
     15     private Node<E> last;
     16 
     17     private static class Node<E>{
     18         E element;
     19         Node<E> next;
     20         Node<E> pre;
     21         Node(Node<E> pre,E element,Node<E> next){
     22             this.element = element;
     23             this.next = next;
     24             this.pre = pre;
     25         }
     26     }
     27 
     28 
     29 
     30     /**
     31      * 在指定索引index处插入指定元素
     32      * @param index 注意:index >= 0 && index <= size (是可以插入到最后一个元素的下一个位置的)
     33      * @param element
     34      */
     35     @Override
     36     public void add(int index, E element) {
     37         checkPositionIndex(index);//判断index >=0 && index <= size
     38 
     39         //方法一:常规思维
     40 //        if (index == 0) {//这里不管first是否为null,都是没有问题的
     41 //            //如果最开始的first为null,则插入的节点为node(element,null),被first指向
     42 //            //如果最开始的first不为null,则插入的节点为node(element,first),除了被first指向,它还指向最开始的那个first
     43 //            first = new Node(element, first);
     44 //        } else {//插入位置索引不是0
     45 //            Node<E> pre = node(index - 1);//前一个节点
     46 //            Node<E> next = pre.next;//后一个节点
     47 //            pre.next = new Node(element,next);//前一个节点指向插入的节点,这个插入的节点指向后一个节点
     48 //        }
     49 //        size++;
     50 
     51         //方法二:代码简化
     52         //思路:获得指定的index的节点 2.前一个节点的获取 3.构建要插入的节点 4.改变1和2的指向
     53         if (index == size) {//涉及到last,包含两种情况:1.原来有元素的时候,新元素添加到末尾  2.原来没有元素的时候
     54             linkLast(element);
     55         } else {//不涉及到last
     56             linkBefore(element,index);
     57         }
     58         size++;
     59 
     60 
     61 
     62     }
     63 
     64     private void linkLast(E element) {
     65         //方法一:常规思维
     66 //        Node newNode;
     67 //        if (size == 0) {//原来没有元素的时候
     68 //            //1.构建要插入的节点newNode,完成它的指向关系
     69 //            newNode = new Node(null, element, null);
     70 //            //2.first指向新节点
     71 //            first = newNode;
     72 //        } else {//原来有元素的时候,新元素添加到末尾
     73 //            //1.拿到原来的最后一个节点
     74 //            Node<E> pre = last;
     75 //            //2.构建要插入的节点newNode,完成它的指向关系
     76 //            newNode = new Node(pre, element, null);
     77 //            //3.将原来的最后一个节点指向新节点
     78 //            pre.next = newNode;
     79 //        }
     80 //        //最后将last指向新节点
     81 //        last = newNode;
     82 
     83         //方法二:代码简化
     84         Node newNode;
     85         //1.拿到last
     86         Node<E> pre = last;
     87         //2.构建要插入的节点newNode,完成它的指向关系
     88         newNode = new Node(pre, element, null);
     89         //3.将last指向新节点
     90         last = newNode;
     91         if (pre == null) {//原来没有元素的时候
     92             //first指向新节点
     93             first = newNode;
     94         } else {//原来有元素的时候,新元素添加到末尾
     95             //将原来的最后一个节点指向新节点
     96             pre.next = newNode;
     97         }
     98 
     99 
    100     }
    101 
    102     private void linkBefore(E element,int index) {
    103         //1.获取索引index处的节点
    104         Node<E> next = node(index);
    105         //2.获取索引(index-1)处的节点
    106         Node<E> pre = next.pre;
    107         //3.构建要插入的节点newNode,完成它的指向关系
    108         Node<E> newNode = new Node(pre, element, next);
    109         //4.索引index处的节点指向新节点
    110         next.pre = newNode;
    111 
    112         if (pre == null) { //索引index==0时
    113             //first指向新节点
    114             first = newNode;
    115         } else {
    116             //索引(index-1)处的节点指向新节点
    117             pre.next = newNode; //为了防止此行空指针异常,所以必须考虑pre === null的情况
    118         }
    119     }
    120 
    121 
    122     private void checkPositionIndex(int index) {
    123         if (!isPositionIndex(index)) {
    124             throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
    125         }
    126     }
    127 
    128     private boolean isPositionIndex(int index) {
    129         return index >= 0 && index <= size;
    130     }
    131 
    132     /**
    133      * 移除集合中指定索引的元素,并返回这个元素
    134      * @param index
    135      * @return
    136      */
    137     @Override
    138     public E remove(int index) {
    139         checkElementIndex(index);
    140         //先从一般的规律入手,写下来,再看特殊情况,
    141         Node<E> node = node(index);
    142         final E element = node.element;
    143         final Node<E> pre = node.pre;
    144         final Node<E> next = node.next;
    145         if (pre == null) { //index == 0
    146             first = next;
    147         } else {
    148             pre.next = next;
    149             //将要移除节点的pre设置为null
    150             node.pre = null;
    151         }
    152         if (next == null) {
    153             last = pre;
    154 
    155         } else {
    156             next.pre = pre;
    157             //将要移除节点的next设置为null
    158             node.next = null;
    159         }
    160         //将要移除节点的element设置为null
    161         node.element = null;
    162         size--;
    163         return element;
    164     }
    165 
    166 
    167 
    168     /**
    169      * 获取指定索引(节点)处的元素
    170      * 因为增删改都是基于先找到元素,所以这个方法最先实现比较好
    171      * @param index
    172      * @return
    173      */
    174     @Override
    175     public E get(int index) {
    176         checkElementIndex(index);
    177         return node(index).element;
    178 
    179     }
    180 
    181     /**
    182      * 检查指定索引,如果集合中不存在这个指定索引,则抛出异常
    183      * @param index
    184      */
    185     private void checkElementIndex(int index) {
    186         if (!isElementIndex(index)) {
    187             throw new IndexOutOfBoundsException("Index: " + index + ",Size: " + size);
    188         }
    189     }
    190 
    191     /**
    192      * 找到指定索引index对应的Node节点
    193      * @param index
    194      * @return
    195      */
    196     private Node<E> node(int index) {
    197         //获取集合第一个元素first
    198         Node<E> x = first;
    199         //先去判断要查找的index是靠近头还是靠近尾,如果靠近头,从头开始找;如果靠近尾,从尾开始找
    200         //如果index小于size的二分之一,则靠近头
    201         //size>>1:向右移动一位,就除以2
    202         if (index < (size >> 1)) {
    203             for (int i = 0; i < index; i++) {
    204                 x = x.next;
    205             }
    206         } else {
    207             x = last;
    208             for (int i = size-1; i > index; i--) {
    209                 x = x.pre;
    210             }
    211         }
    212 
    213         return x;
    214     }
    215 
    216     /**
    217      * 判断集合中是否存在指定索引
    218      * @param index
    219      * @return
    220      */
    221     private boolean isElementIndex(int index) {
    222         return index >= 0 && index < size;
    223     }
    224 
    225 
    226     /**
    227      * 将指定索引处的元素替换为指定的元素
    228      * @param index 指定的索引
    229      * @param element 将要替换到指定索引处的元素
    230      * @return 返回指定索引处被替换的元素
    231      */
    232     @Override
    233     public E set(int index, E element) {
    234         checkElementIndex(index);
    235         Node<E> node = node(index);
    236         E oldEle = node.element;
    237         node.element = element;
    238         return oldEle;
    239     }
    240 
    241     /**
    242      * 查找第一次出现指定元素o的索引位置
    243      * @param o
    244      * @return 如果有,返回o在集合中的索引;如果没有,返回-1
    245      */
    246     @Override
    247     public int indexOf(Object o) {
    248         int index = 0;
    249         if (o == null) {
    250             //多去理解这种遍历思维
    251             for (Node<E> x = first; x != null; x = x.next) {
    252                 if (x.element == o) {
    253                     return index;
    254                 }
    255                 //还有这种返回索引的思维也要多学习
    256                 index++;
    257             }
    258         } else {
    259             for (Node<E> x = first; x != null; x = x.next) {
    260                 if (o.equals(x.element)) {
    261                     return index;
    262                 }
    263                 index++;
    264             }
    265         }
    266         return -1;
    267     }
    268 
    269     /**
    270      * 清空集合的所有元素
    271      *
    272      * 可达性算法:判断对象是否是一个垃圾的标准
    273      *  选取一个节点,作为GC ROOTS顶点,其他对象或者引用去指向这个GC ROOTS顶点,如果这些对象
    274      *  能够到达这个GC ROOTS顶点,那么这些对象不是垃圾,反之就是。
    275      *
    276      */
    277     @Override
    278     public void clear() {
    279         //遍历所有的节点,只要不为null,就将其pre,next,element都设置为null
    280         for (Node<E> x = first; x != null;) {
    281             Node<E> next = x.next;
    282             x.pre = null;
    283             x.next = null;
    284             x.element = null;
    285             x = next;
    286         }
    287         size = 0;
    288         first = null;
    289         last = null;
    290     }
    291 
    292     @Override
    293     public String toString() {
    294         if (size == 0) {
    295             return "[]";
    296         }
    297         StringBuilder sb= new StringBuilder().append('[');
    298         for( Node<E> x = first;x!=null;x=x.next){
    299             sb.append(x.element);
    300             if (x.next == null) {
    301                 return sb.append(']').toString();
    302             }
    303             sb.append(',').append(' ');
    304         }
    305         return sb.toString();
    306     }
    307 }
  • 相关阅读:
    给你的程序增加热键(C#)
    C#中的键盘处理
    可以给img元素设置背景图
    如何利用JS实现对后台CS代码的调用
    李阳疯狂英语300句
    如何基于linux创造财富
    3d材质贴图常用参数
    asp.net下检测远程URL是否存在的三种方法
    ASP.NET 配置
    服务器硬盘空间操作
  • 原文地址:https://www.cnblogs.com/zui-ai-java/p/14461050.html
Copyright © 2020-2023  润新知