一、LinkedList简介
LinkedList是一种可以在任何位置进行高效地插入和移除操作的有序序列,它是基于双向链表实现的。
ps:这里有一个问题,就是关于实现LinkedList的数据结构是否为循环的双向链表,上网搜了有很多文章都说是循环的,并且有的文章中但是我看了源代码觉得应该不是循环的?
例如在删除列表尾部节点的代码:
private E unlinkLast(Node<E> l)
{ final E element = l.item; final Node<E> prev = l.prev; l.item = null; l.prev = null; // help GC last = prev; if (prev == null) first = null; else prev.next = null; size--; modCount++; return element; }
这里删除尾节点l后,将l前面的节点prev的next置为null,而并没有指向head节点。不知道是不是因为代码版本的原因(我的源代码是在下载的jdk1.8.0_45文件中获取的),如果读者看到知道原因,希望能够帮忙解答!
在源码中定义了节点的基本结构:
private static class Node<E> { E item; //表示该节点包含的值 Node<E> next; //表达当前节点的下一个节点 Node<E> prev; //表示当前节点的上一个节点 Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
LinkedList的类图如下所示:
LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
LinkedList 实现 List 接口,能对它进行队列操作。
LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
LinkedList 是非同步的。
二、LinkedList源码分析
1 public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable 2 { 3 //实现Serilizable接口时,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。 4 transient int size = 0; 5 //指向首节点 6 transient Node<E> first; 7 //指向最后一个节点 8 transient Node<E> last; 9 //构建一个空列表 10 public LinkedList() { 11 } 12 //构建一个包含集合c的列表 13 public LinkedList(Collection<? extends E> c) { 14 this(); 15 addAll(c); 16 } 17 //将节点值为e的节点作为首节点 18 private void linkFirst(E e) { 19 final Node<E> f = first; 20 //构建一个prev值为null,next为f,节点值为e的节点 21 final Node<E> newNode = new Node<>(null, e, f); 22 //将newNode作为首节点 23 first = newNode; 24 //如果newNode后面没有节点就将newNode作为最后一个节点 25 if (f == null) 26 last = newNode; 27 //否则就将newNode赋给其prev 28 else 29 f.prev = newNode; 30 //列表长度加一 31 size++; 32 modCount++; 33 } 34 //将节点值为e的节点作为最后一个节点 35 void linkLast(E e) { 36 final Node<E> l = last; 37 //构建一个prev值为l,next为null,节点值为e的节点 38 final Node<E> newNode = new Node<>(l, e, null); 39 last = newNode; 40 if (l == null) 41 first = newNode; 42 else 43 l.next = newNode; 44 size++; 45 modCount++; 46 } 47 //在非空节点succ之前插入节点e 48 void linkBefore(E e, Node<E> succ) { 49 final Node<E> pred = succ.prev; 50 //将succ前面的节点和succ作为其prev和next 51 final Node<E> newNode = new Node<>(pred, e, succ); 52 //然后将newNode作为succ的prev 53 succ.prev = newNode; 54 //如果原来succ是首节点,则将newNode设置为首节点 55 if (pred == null) 56 first = newNode; 57 else 58 pred.next = newNode; 59 size++; 60 modCount++; 61 } 62 //释放非空的首节点f 63 private E unlinkFirst(Node<E> f) { 64 final E element = f.item; 65 final Node<E> next = f.next; 66 f.item = null; 67 f.next = null; // help GC 68 //将first节点后面的节点设为首节点 69 first = next; 70 if (next == null) 71 last = null; 72 else 73 next.prev = null; 74 size--; 75 modCount++; 76 return element; 77 } 78 //释放非空的尾节点l 79 private E unlinkLast(Node<E> l) { 80 final E element = l.item; 81 final Node<E> prev = l.prev; 82 l.item = null; 83 l.prev = null; // help GC 84 last = prev; 85 if (prev == null) 86 first = null; 87 else 88 prev.next = null; 89 size--; 90 modCount++; 91 return element; 92 } 93 //删除非空节点x 94 E unlink(Node<E> x) 95 { 96 final E element = x.item; 97 final Node<E> next = x.next; //x后面的节点 98 final Node<E> prev = x.prev; //x前面的节点 99 100 if (prev == null) { 101 first = next; 102 } else { 103 prev.next = next; 104 x.prev = null; 105 } 106 if (next == null) { 107 last = prev; 108 } else { 109 next.prev = prev; 110 x.next = null; 111 } 112 x.item = null; 113 size--; 114 modCount++; 115 return element; 116 } 117 //返回列表首节点元素值 118 public E getFirst() { 119 final Node<E> f = first; 120 if (f == null) 121 throw new NoSuchElementException(); 122 return f.item; 123 } 124 //返列表尾节点元素值 125 public E getLast() { 126 final Node<E> l = last; 127 if (l == null) 128 throw new NoSuchElementException(); 129 return l.item; 130 } 131 //移除首节点 132 public E removeFirst() { 133 final Node<E> f = first; 134 if (f == null) 135 throw new NoSuchElementException(); 136 return unlinkFirst(f); 137 } 138 //删除尾节点 139 public E removeLast() { 140 final Node<E> l = last; 141 if (l == null) 142 throw new NoSuchElementException(); 143 return unlinkLast(l); 144 } 145 //在列表首部插入节点e 146 public void addFirst(E e) { 147 linkFirst(e); 148 } 149 //在列表尾部插入节点e 150 public void addLast(E e) { 151 linkLast(e); 152 } 153 //判断列表中是否包含有元素o 154 public boolean contains(Object o) { 155 return indexOf(o) != -1; 156 } 157 //返回列表长度大小 158 public int size() { 159 return size; 160 } 161 //在列表尾部插入元素 162 public boolean add(E e) { 163 linkLast(e); 164 return true; 165 } 166 //删除元素为o的节点 167 public boolean remove(Object o) 168 { 169 if (o == null) { 170 for (Node<E> x = first; x != null; x = x.next) { 171 if (x.item == null) { 172 unlink(x); 173 return true; 174 } 175 } 176 } else { 177 for (Node<E> x = first; x != null; x = x.next) { 178 if (o.equals(x.item)) { 179 unlink(x); 180 return true; 181 } 182 } 183 } 184 return false; 185 } 186 //将集合c中所有元素添加到列表的尾部 187 public boolean addAll(Collection<? extends E> c) { 188 return addAll(size, c); 189 } 190 //从指定的位置index开始,将集合c中的元素插入到列表中 191 public boolean addAll(int index, Collection<? extends E> c) { 192 //首先判断插入位置的合法性 193 checkPositionIndex(index); 194 Object[] a = c.toArray(); 195 int numNew = a.length; 196 if (numNew == 0) 197 return false; 198 Node<E> pred, succ; 199 if (index == size) {//说明在列表尾部插入集合元素 200 succ = null; 201 pred = last; 202 } 203 else { //否则,找到index所在的节点 204 succ = node(index); 205 pred = succ.prev; 206 } 207 for (Object o : a) { 208 @SuppressWarnings("unchecked") E e = (E) o; 209 Node<E> newNode = new Node<>(pred, e, null); 210 if (pred == null) 211 first = newNode; 212 else 213 pred.next = newNode; 214 pred = newNode; 215 } 216 if (succ == null) { 217 last = pred; 218 } else { 219 pred.next = succ; 220 succ.prev = pred; 221 } 222 size += numNew; 223 modCount++; 224 return true; 225 } 226 //删除列表中所有节点 227 public void clear() { 228 for (Node<E> x = first; x != null; ) 229 { 230 Node<E> next = x.next; 231 x.item = null; 232 x.next = null; 233 x.prev = null; 234 x = next; 235 } 236 first = last = null; 237 size = 0; 238 modCount++; 239 } 240 //获取指定索引位置节点的元素值 241 public E get(int index) { 242 checkElementIndex(index); 243 return node(index).item; 244 } 245 //替换指定索引位置节点的元素值 246 public E set(int index, E element) { 247 checkElementIndex(index); 248 Node<E> x = node(index); 249 E oldVal = x.item; 250 x.item = element; 251 return oldVal; 252 } 253 //在指定索引位置之前插入元素e 254 public void add(int index, E element) { 255 checkPositionIndex(index); 256 if (index == size) 257 linkLast(element); 258 else 259 linkBefore(element, node(index)); 260 } 261 //删除指定位置的元素 262 public E remove(int index) { 263 checkElementIndex(index); 264 return unlink(node(index)); 265 } 266 //判断指定索引位置的元素是否存在 267 private boolean isElementIndex(int index) { 268 return index >= 0 && index < size; 269 } 270 private boolean isPositionIndex(int index) { 271 return index >= 0 && index <= size; 272 } 273 //构建 IndexOutOfBoundsException详细信息 274 private String outOfBoundsMsg(int index) { 275 return "Index: "+index+", Size: "+size; 276 } 277 private void checkElementIndex(int index) { 278 if (!isElementIndex(index)) 279 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 280 } 281 private void checkPositionIndex(int index) { 282 if (!isPositionIndex(index)) 283 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 284 } 285 //返回指定索引位置的节点 286 Node<E> node(int index) { 287 //此处是一个小技巧,当index<size/2时,从列表前半部分开始,否则从后半部分开始 288 if (index < (size >> 1)) { 289 Node<E> x = first; 290 for (int i = 0; i < index; i++) 291 x = x.next; 292 return x; 293 } else { 294 Node<E> x = last; 295 for (int i = size - 1; i > index; i--) 296 x = x.prev; 297 return x; 298 } 299 }//返回列表中第一次出现o的位置,若不存在则返回-1 300 public int indexOf(Object o) { 301 int index = 0; 302 if (o == null) { 303 for (Node<E> x = first; x != null; x = x.next) { 304 if (x.item == null) 305 return index; 306 index++; 307 } 308 } else { 309 for (Node<E> x = first; x != null; x = x.next) { 310 if (o.equals(x.item)) 311 return index; 312 index++; 313 } 314 } 315 return -1; 316 } 317 //逆向搜索,返回第一出现o的位置,不存在则返回-1 318 public int lastIndexOf(Object o) { 319 int index = size; 320 if (o == null) { 321 for (Node<E> x = last; x != null; x = x.prev) { 322 index--; 323 if (x.item == null) 324 return index; 325 } 326 } else { 327 for (Node<E> x = last; x != null; x = x.prev) { 328 index--; 329 if (o.equals(x.item)) 330 return index; 331 } 332 } 333 return -1; 334 } 335 //获取列表首节点元素值 336 public E peek() { 337 final Node<E> f = first; 338 return (f == null) ? null : f.item; 339 } 340 341 //获取列表首节点元素值,若为空则抛异常 342 public E element() { 343 return getFirst(); 344 } 345 //检索首节点,若空则返回null,不为空则返回其元素值并删除首节点 346 public E poll() { 347 final Node<E> f = first; 348 return (f == null) ? null : unlinkFirst(f); 349 } 350 //检索首节点,若空则抛异常,不为空则返回其元素值并删除首节点 351 public E remove() { 352 return removeFirst(); 353 } 354 //在列表尾部增加节点e 355 public boolean offer(E e) { 356 return add(e); 357 } 358 //在列表首部插入节点e 359 public boolean offerFirst(E e) { 360 addFirst(e); 361 return true; 362 } 363 //在列表尾部插入节点e 364 public boolean offerLast(E e) { 365 addLast(e); 366 return true; 367 } 368 public E peekFirst() { 369 final Node<E> f = first; 370 return (f == null) ? null : f.item; 371 } 372 //获取列表尾节点元素值 373 public E peekLast() { 374 final Node<E> l = last; 375 return (l == null) ? null : l.item; 376 } 377 public E pollFirst() { 378 final Node<E> f = first; 379 return (f == null) ? null : unlinkFirst(f); 380 } 381 public E pollLast() { 382 final Node<E> l = last; 383 return (l == null) ? null : unlinkLast(l); 384 } 385 //入栈 386 public void push(E e) 387 { 388 addFirst(e); 389 } 390 //出栈 391 public E pop() { 392 return removeFirst(); 393 } 394 //删除列表中第一出现o的节点 395 public boolean removeFirstOccurrence(Object o) { 396 return remove(o); 397 } 398 //逆向搜索,删除第一次出现o的节点 399 public boolean removeLastOccurrence(Object o) { 400 if (o == null) { 401 for (Node<E> x = last; x != null; x = x.prev) { 402 if (x.item == null) { 403 unlink(x); 404 return true; 405 } 406 } 407 } else { 408 for (Node<E> x = last; x != null; x = x.prev) { 409 if (o.equals(x.item)) { 410 unlink(x); 411 return true; 412 } 413 } 414 } 415 return false; 416 }
三、关于LinkedList的几点说明
1、注意源码中的 Node<E> node(int index)方法:
Node<E> node(int index) { if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
该方法返回双向链表中指定位置处的节点,而链表中是没有下标索引的,要指定位置出的元素,就要遍历该链表,从源码的实现中,我们看到这里有一个加速动作。源码中先将index与长度size的一半比较,如果index<size/2,就只从位置0往后遍历到位置index处,而如果index>size/2,就只从位置size往前遍历到位置index处。这样可以减少一部分不必要的遍历。
2、LinkedList与ArrayList的区别:
LinkedList与ArrayList在性能上各有优缺点,都有各自适用的地方,总结如下:
- ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
- LinkedList不支持高效的随机元素访问。
- ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间,就存储密度来说,ArrayList是优于LinkedList的。
总之,当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能,当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。
3、LinkedList中允许元素为null
在查找和删除时,源代码如下所示:
public int indexOf(Object o) { int index = 0; if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) return index; index++; } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) return index; index++; } } return -1; }
4、利用LinkedList实现栈操作
public class Stack<T> { private LinkedList<T> stack; //无参构造函数 public Stack() { stack=new LinkedList<T>(); } //构造一个包含指定collection中所有元素的栈 public Stack(Collection<? extends T> c) { stack=new LinkedList<T>(c); } //入栈 public void push(T t) { stack.addFirst(t); } //出栈 public T pull() { return stack.remove(); } //栈是否为空 boolean isEmpty() { return stack.isEmpty(); } //打印栈元素 public void display() { for(Object o:stack) System.out.println(o); } }