自定义(双链表)集合类
包结构
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 }