LinkedList
双链(操作头尾元素极快)
修改次数变动:add、addAll(一次)、remove、clear
先写节点类,再写在头尾(节点)添加元素,在节点前数据,生成添加方法,再写删除头尾节点返回数据,再写删除某一元素(不需要查找元素,单纯删除元素),再写查找头尾节点,根据索引查找节点
以此生成增删改查方法(先对节点进行操作)
一、 创建节点类型(定义成内部类)
节点内定义头节点(节点类型),尾节点(节点类型),数据以及长度和构造器
//头节点和尾节点分别储存前后地址
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; } }
二、元素添加
实现增删改查前先定义了头元素、尾元素和指定索引在该索引前添加元素的步骤
头元素添加
先将头元素取出用f存放,再创建新对象,同时在满参构造中把f交给新对象,接下来让头节点等于新节点,最后判断后节点是否为空的情况(即集合是否是空)如果不是则将f的前节点指向新对象,长度加一,并且修改次数modCount+1
private void linkFirst(E e) { final Node<E> f = first; final Node<E> newNode = new Node<>(null, e, f); first = newNode; if (f == null) last = newNode; else f.prev = newNode; size++; modCount++; }
尾元素添加:
将尾元素取出用l存放,创建新对象并将原尾元素对象存入新对象的头节点中,尾节点等于新的尾节点,接下来进行判断是否为空集合,如果空结合即头节点等于该节点,否则l的尾节点指向新节点地址,长度+1,并且修改次数modCount+1
void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
中间元素添加
指定节点在该节点位置前进行添加元素
首先将前节点用pred保存,然后创建节点并将前后地址存入该
节点,接下来让后元素的前节点指向该节点,进行判断是否为空集合,是的话直接让该集合为前节点,不是的话让pred的后节点指向该节点,长度+1,并且修改次数modCount+1
void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; }
指定索引添加数据
先进行判断,然后判断添加是否为尾部,尾部直接调用尾部方法,不是则掉用中间添加元素方法
public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); }
添加元素集合(addAll(索引,元素集合))
boolean addAll(int index, Collection<? extends E> c)
添加的是Collection集合,可以添加的是所有集合,且集合泛型必须是E 的子类或者子类的子类,(后面会执行强转)
仅将集合的数据进行添加
仅将集合的数据进行添加
仅将集合的数据进行添加
先对索引进行判断,大于size小于0返回索引越界异常
将集合c转成Object数组a,判断数组长度,长度为0直接返回false
创建节点pred(前)和succ(后),pred主要用记录现在的节点(当遍历时pred一直随遍历而动),而succ记录的是现在索引处的节点(保持不变)
对索引进行判断,如果索引位置是末尾,则succ(后)为null,pred(前)为last,否则的话succ(后)为调用node()方法得到index处的节点,(即将c集合的尾元素和原数组的元素进行连接),遍历a集合得到所有数据,得到每一个数据并强转成e类型,(因为遍历的是所有集合,要求一个个遍历而不是直接加),创建节点对象,在创建时将数据放入节点中,且前节点地址为pred,后节点为null,如果前节点为空,则将该节点定义为头节点,否则前面节点的后节点为该节点
遍历完成后如果后节点为空则说明是最后一个节点,尾节点设为该节点,不为该节点的后节点尾succ,succ的前节点为pred
长度+该集合长度,修改次数modCount只是+1
public boolean addAll(int index, Collection<? extends E> c) { checkPositionIndex(index); Object[] a = c.toArray(); int numNew = a.length; if (numNew == 0) return false; Node<E> pred, succ; if (index == size) { succ = null; pred = last; } else { succ = node(index); pred = succ.prev; } for (Object o : a) { @SuppressWarnings("unchecked") E e = (E) o; Node<E> newNode = new Node<>(pred, e, null); if (pred == null) first = newNode; else pred.next = newNode; pred = newNode; } if (succ == null) { last = pred; } else { pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true; }
元素删除
删除头元素
(unlinkFirst(Node<E> f))返回该元素数据
首先拿出f的数据用element存储用以返回,然后用next(f后的节点)储存接下来的节点,然后f的数据null并将f的next(f内的next地址)节点数据设置为空,将next设置成前节点,判断后节点是否为空,为空则说明是最后一个元素,直接让该next(f后节点)为尾节点,否则让下一个节点的前节点地址为空,长度-1,并且修改次数modCount+1
private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }
删除尾元素
(unlinkFirst(Node<E> l))返回该元素数据
先用element储存f的数据,然后将l的前节点用prev储存,将l元素的数据和前节点设置为空,然后判断节点是否为空,是空直接将头节点设为空,否则前节点的next地址设为null,长度-1,并且修改次数modCount+1
private E unlinkLast(Node<E> l) { // assert l == last && l != null; 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; }
删除元素
E unlink(Node<E> x)
首先用element储存元素数据用以返回,创建对象prev,next分别储存前后节点,判断前节点是否为空,为空则头节点为空,不是则前节点的next为x的next,x的前节点为空,接下来判断后节点是否为空,是的话让尾节点为x的前节点,不是的话让x后的节点的前节点指向x前节点,x的后节点为null,最后x的数据设为空,长度-1,并且修改次数modCount+1
E unlink(Node<E> x) { // assert x != null; final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
删除元素(remove(Object o))
首先判断o是否为null如果是则从头节点开始遍历,用==进行判断,如果元素数据为null则引用unlink(x)返回true,
如果不是则遍历用equals判断相等调用unlink方法返回ture,
否则都找不到返回flase
public boolean remove(Object o) { if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { unlink(x); return true; } } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; }
清空元素
clear方法:
使用next从first进行遍历,每当遍历一个就将元素删除,遍历完成后first = last = null; size = 0;修改次数modCount次数+1
public void clear() { for (Node<E> x = first; x != null; ) { Node<E> next = x.next; x.item = null; x.next = null; x.prev = null; x = next; } first = last = null; size = 0; modCount++; }
元素查询
查头元素
创建节点f等于first
判断f是否为空,为空返回NoSuchElementException();错误,否则返回f的数据
public E getFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return f.item; }
查尾元素
同理
public E getLast() { final Node<E> l = last; if (l == null) throw new NoSuchElementException(); return l.item; }
通过索引查询元素
Node<E> node(int index){}
判断索引是否大于长度的一半,小于则从前遍历,大于则从后遍历
注意小于一半用(size >>1)
Node<E> node(int index) { // assert isElementIndex(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; } }
public E get(int index){}
先进行判断索引(定义成方法,判断是否索引越界)
调用node()方法:(实现通过索引查到节点)得到节点调用里面数据返回;
public E get(int index) { checkElementIndex(index); return node(index).item; }
根据元素查询索引
int indexOf(Object o){}返回值为int类型,未找到返回-1
先对o进行判断是否为null,如果是则从头节点开始遍历,用 == 进行判断,如果元素数据为null则引用unlink(x)返回true,
如果不是则遍历用 equals 判断相等调用unlink方法返回ture,(先进行判断在进行--操作)
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; }
int lastIndexof(Object o ){}
从尾部开始遍历查找(index = size,后续先进行--再进行判断)
修改元素
元素修改
set返回删除元素数据
先判断索引,创建节点对象为索引处元素,将元素的数组取出用E对象保存,将新数据翻进去(修改次数未变)
public E set(int index, E element) { checkElementIndex(index); Node<E> x = node(index); E oldVal = x.item; x.item = element; return oldVal; }
clone
superClone()
调用本地clone技术,将本地clone后的强转成LinkedList<E>
?? 将调用该方法的集合整体进行克隆,返回是一个集合
private LinkedList<E> superClone() { try { return (LinkedList<E>) super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(e); } }
不懂
//clone内头和尾本来就是null,size为什么改为0
clone
首先创建集合对象接受调用superClone返回的集合
调用克隆返回指向地址的新对象
public Object clone() { LinkedList<E> clone = superClone(); // Put clone into "virgin" state //将克隆设为处女状态, //头和尾本来就是null,size为什么改为0 clone.first = clone.last = null; clone.size = 0; clone.modCount = 0; // Initialize clone with our elements for (Node<E> x = first; x != null; x = x.next) clone.add(x.item); return clone; }
转成Object数组
创建集合长度的数组,然后进行遍历,将节点的数据存进数组内
public Object[] toArray() { Object[] result = new Object[size]; int i = 0; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; return result; }
不懂
<T> T[] toArray(T[] a){}
public <T> T[] toArray(T[] a) { if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); int i = 0; Object[] result = a; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; if (a.length > size) a[size] = null; return a; }
迭代器
迭代器主要用以遍历,其中add和remove方法在遍历的基础上进行
当调用add后lastReturned会变成null,不能进行remove操作
ListItr
成员变量,构造方法
构造器,如果索引为size则next = null;不为size则next = index处节点,nextIndex为index,调用时必须传入index
如果索引等于size则说明是在链条尾部接下来也会从尾部进行操作
大部分方法都会判断并发异常:expectedModCount = modCount期待改变和实际改变不相等
expectedModeCount是在迭代器内的修改次数
private Node<E> lastReturned;//临时变量使用next时不用创建节点,直接将当前节点赋值给此节点,而next则指向下一个节点 private Node<E> next; private int nextIndex; private int expectedModCount = modCount; ListItr(int index) { // assert isPositionIndex(index); next = (index == size) ? null : node(index); nextIndex = index; }
hasNext():
索引小于size则有索引,返回值为boolean
public boolean hasNext() { return nextIndex < size; }
next() (向后遍历)
进行判断如果expectedModCount != modCount则并发异常
判断是否hasNext(),如果没有则没有找到元素异常
将lastReturned等于next,next为接下来的节点,索引增加 1,返回lastReturned节点的数据
public E next() { checkForComodification(); if (!hasNext()) throw new NoSuchElementException(); lastReturned = next; next = next.next; nextIndex++; return lastReturned.item; }
hasPrevious()
判断前面是否有元素
public boolean hasPrevious() { return nextIndex > 0; }
E Previous() (先前遍历)
进行判断如果expectedModCount != modCount则并发异常
判断是否hasPrevious(),如果没有则没有找到元素异常
next是否为null,如果为空则让next和lastReturned等于last
如果不为空则让next和lastReturned等于next的prev;
索引-1,返回该处节点数据
public E previous() { checkForComodification(); if (!hasPrevious()) throw new NoSuchElementException(); lastReturned = next = (next == null) ? last : next.prev; nextIndex--; return lastReturned.item; }
add(E e)
判断并发异常
让lastReturn为null,如果next为null则说明是元素的尾部(index == size),调用linkLast方法在链条尾部添加数据e,如果不是则说明是在next位置前添加元素
调用linkBefore方法,然后内部索引+1,期待改变次数+1,调用时linkLast或LinkBefore时modCount会++,且链条长度会+1
public void add(E e) { checkForComodification(); lastReturned = null; if (next == null) linkLast(e); else linkBefore(e, next); nextIndex++; expectedModCount++; }
remove
判断并发异常
判断lastReturened是否为空,删除操作建立于上次操作元素的基础上
创建临时节点lastNext储存接下来的节点,调用unlike()方法删除该元素,判断next是否为删除的节点,是的话让next指向lastNext,
remove问题:
?lastReturned == null啥意思,为什么报错
//lastReture存放上次删除的节点,删除的操作建立在遍历的基础上,删除的就是上次操作的节点,当调用add方法时lastReturned会进行重置,即等于null,此时只能进行遍历而不再可以进行remove操作,remove操作前提是确定上次操作的元素(即lastReturned的节点),是将该节点删除
?为什么next == lastRetured就不用做 -- 操作
此处判断的是调用previous还是next方法,
当调用previous时,从后往前遍历,删除节点,next应当回到后一个节点,以便下一次进行遍历不会减少节点;
当调用next方法时,从前往后遍历,节点删除,节点索引的要回退一步否则少遍历一个节点,进行减减操作,类似于ArrayList集合遍历时调用remove方法
当调用add方法时说明上次没有遍历元素,将lastReturned重回null(强调迭代器用以遍历不适合添加节点和删除节点)
否则nextIndex-1,将lastReturn为空,期望改变次数+1
调用unlike()方法,然后内部索引+1,改变次数+1,
public void remove() { checkForComodification(); if (lastReturned == null) throw new IllegalStateException(); Node<E> lastNext = lastReturned.next; unlink(lastReturned); //判断是previous还是next方法 if (next == lastReturned) next = lastNext; else nextIndex--; lastReturned = null; expectedModCount++; }
set
判断lastReturned是否为空,为空抛出异常
判断并发异常
直接将lastReturned位置的数据改成e
public void set(E e) { if (lastReturned == null) throw new IllegalStateException(); checkForComodification(); lastReturned.item = e; }
不懂:
Consumer类:accept
forEachRemaining(Consumer<? super E> action){}
必须是Consumer接口类型或者子类实现类泛型必须是E的父类
判断action是否为空,
当实际修改次数等于类中修改次数并且类内索引小于size
public void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); while (modCount == expectedModCount && nextIndex < size) { action.accept(next.item); lastReturned = next; next = next.next; nextIndex++; } checkForComodification(); }
DescendingIterator
创建ListItr进行操作,调用ListItr方法
private class DescendingIterator implements Iterator<E> { private final ListItr itr = new ListItr(size()); public boolean hasNext() { return itr.hasPrevious(); } public E next() { return itr.previous(); } public void remove() { itr.remove(); } }