LinkedList

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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值