LinkedList

LinkedList概念与简单实例

LinkedList类是双向列表,列表中的每个节点都包含了对前一个和后一个元素的引用.

  • 1:LinkedList提供了4个不同位置的添加数据的方法,分别为链头插入,链尾插入,节点前插入,节点后插入
  • 2:由于LinkedList是双向链表,在查询数据方面提供了“从前往后”和“从后往前”两个查询方法
  • 3:如果数据量大,删除频繁,只能用LinkedList。

特别注意:
list.get(i),LinkedList的底层是一个链表,随机访问i的时候,链表只能从头往后数,第i个才返回。所以时间随着i的变大时间会越来越长。
因此在遍历的时候使用:

for (String element : list) {
    // process element here
}
Iterator<String> iter = list.iterator();  
while (iter.hasNext()) {  
    String element = iter.next();  
    // process element here  
}  

实例1:用LinkedList实现栈

import java.util.LinkedList;

public class StackByLinkedList<T> {

    private LinkedList<T> stack=new LinkedList<>();

    public void push(T v)
    {
        stack.addFirst(v);
    }
    public T top()
    {
        return stack.getFirst();
    }
    public T pop()
    {
        return stack.removeFirst();
    }
}

实例2:用LinkedList实现队列

public class QueueByLinkedList<T>{
    
    private LinkedList<T> list = new LinkedList<>();
     public void put (T v){
         list.addFist(v);
     }
     public T get(){
         return list.removeLast();
     }
     public boolean isEmpty(){
         return list.isEmpty();
     }
}

实例3:实现优先级链表

package com.csu.collection;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

/**
 * 优先级链表的实现原理是:使用在数组中存储链表的方法,然后依据优先级初始化数组,即有多少级别,数组的长度就为多少
 *
 */
public class BasicPriorityLinkedList {
protected LinkedList[] linkedLists;
  protected int priorities;
  protected int size;


  public BasicPriorityLinkedList(int priorities) {
    this.priorities = priorities;
    initDeques();
  }
  /**
   * 依据优先级,对应插入数据
   * @param obj
   * @param priority
   */
  public void addFirst(Object obj, int priority) {
    linkedLists[priority].addFirst(obj);
    size++;
  }
  public void addLast(Object obj, int priority) {
    linkedLists[priority].addLast(obj);
    size++;
  }
  /**
   * 从优先级高的开始查询并删除
   * @return
   */
  public Object removeFirst() {
    Object obj = null;
    for (int i = priorities - 1; i >= 0; i--) {
      LinkedList ll = linkedLists[i];
      if (!ll.isEmpty()) {
        obj = ll.removeFirst();
        break;
      }
    }
    if (obj != null) {
      size--;
    }
    return obj;
  }
  /**
   * 从优先级低的开始查询并删除
   * @return
   */
  public Object removeLast() {
    Object obj = null;
    for (int i = 0; i < priorities; i++) {
      LinkedList ll = linkedLists[i];
      if (!ll.isEmpty()) {
        obj = ll.removeLast();
      }
      if (obj != null) {
        break;
      }
    }
    if (obj != null) {
      size--;
    }
    return obj;
  }


  public Object peekFirst() {
    Object obj = null;
    for (int i = priorities - 1; i >= 0; i--) {
      LinkedList ll = linkedLists[i];
      if (!ll.isEmpty()) {
        obj = ll.getFirst();
      }
      if (obj != null) {
        break;
      }
    }
    return obj;
  }


  public List getAll() {
    List all = new ArrayList();
    for (int i = priorities - 1; i >= 0; i--) {
      LinkedList deque = linkedLists[i];
      all.addAll(deque);
    }
    return all;
  }


  public void clear() {
    initDeques();
  }


  public int size() {
    return size;
  }


  public boolean isEmpty() {
    return size == 0;
  }


  public ListIterator iterator() {
    return new PriorityLinkedListIterator(linkedLists);
  }


  protected void initDeques() {
    linkedLists = new LinkedList[priorities];
    for (int i = 0; i < priorities; i++) {
      linkedLists[i] = new LinkedList();
    }
    size = 0;
  }


  class PriorityLinkedListIterator implements ListIterator {
    private LinkedList[] lists;
    private int index;
    private ListIterator currentIter;
    PriorityLinkedListIterator(LinkedList[] lists) {
      this.lists = lists;
      index = lists.length - 1;
      currentIter = lists[index].listIterator();
    }


    public void add(Object arg0) {
      throw new UnsupportedOperationException();
    }


    public boolean hasNext() {
      if (currentIter.hasNext()) {
        return true;
      }
      while (index >= 0) {
        if (index == 0 || currentIter.hasNext()) {
          break;
        }
        index--;
        currentIter = lists[index].listIterator();
      }
      return currentIter.hasNext();
    }


    public boolean hasPrevious() {
      throw new UnsupportedOperationException();
    }


    public Object next() {
      if (!hasNext()) {
        throw new NoSuchElementException();
      }
      return currentIter.next();
    }


    public int nextIndex() {
      throw new UnsupportedOperationException();
    }


    public Object previous() {
      throw new UnsupportedOperationException();
    }


    public int previousIndex() {
      throw new UnsupportedOperationException();
    }


    public void remove() {
      currentIter.remove();
      size--;
    }


    public void set(Object obj) {
      throw new UnsupportedOperationException();
    }
  }
}

LinkedList源码分析

  • LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
  • LinkedList 实现 List 接口,能对它进行队列操作。
  • LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
  • LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆
  • LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
  • LinkedList 是非同步的。

(1)私有属性

private transient Entry<E> header = new Entry<E>(null,null,null);
private transient int size = 0;

Entry类的代码

private static class Entry<E>{
    E element;
    Entry<E> next;
    Entry<E> previous;
    
    Entry(E element,Entry<E> next, Entry<E> previous){
        this.element = element;
        this.next = next;
        this.previous = previous;
    }
}

(2)构造方法

public LinkedList() {
    header.next = header.previous = header;
}
public LinkedList(Collection<? extends E> c) {
    this();
   addAll(c);
}

(3)元素添加

public boolean addAll(Collection<? extends E>c){
    return addAll(size,c);
}

// index参数指定collection中插入的第一个元素的位置
public boolean addAll(int index, Collection<? extends E> c) {
    // 插入位置超过了链表的长度或小于0,报IndexOutOfBoundsException异常
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index+
                                                ", Size: "+size);
    Object[] a = c.toArray();
   int numNew = a.length;
   // 若需要插入的节点个数为0则返回false,表示没有插入元素
    if (numNew==0)
        return false;
    modCount++;//否则,插入对象,链表修改次数加1
    // 保存index处的节点。插入位置如果是size,则在头结点前面插入,否则在获取index处的节点插入
    Entry<E> successor = (index==size ? header : entry(index));
    // 获取前一个节点,插入时需要修改这个节点的next引用
    Entry<E> predecessor = successor.previous;
    // 按顺序将a数组中的第一个元素插入到index处,将之后的元素插在这个元素后面
    for (int i=0; i<numNew; i++) {
        // 结合Entry的构造方法,这条语句是插入操作,相当于C语言中链表中插入节点并修改指针
        Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);
        // 插入节点后将前一节点的next指向当前节点,相当于修改前一节点的next指针
        predecessor.next = e;
        // 相当于C语言中成功插入元素后将指针向后移动一个位置以实现循环的功能
        predecessor = e;
  }
    // 插入元素前index处的元素链接到插入的Collection的最后一个节点
    successor.previous = predecessor;
    // 修改size
    size += numNew;
    return true;
}

调用到的entry(index)

private Entry<E> entry(int index) {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException("Index: "+index+
                                                ", Size: "+size);
        Entry<E> e = header;
        // 根据这个判断决定从哪个方向遍历这个链表
        // 若index < 双向链表长度的1/2,则从前先后查找;    
        if (index < (size >> 1)) {
            for (int i = 0; i <= index; i++)
                e = e.next;
        } else {
            // 可以通过header节点向前遍历,说明这个一个循环双向链表,header的previous指向链表的最后一个节点,这也验证了构造方法中对于header节点的前后节点均指向自己的解释
            for (int i = size; i > index; i--)
                e = e.previous;
       }
        return e;
    }

(4)添加数据

// 将元素(E)添加到LinkedList中
     public boolean add(E e) {
         // 将节点(节点数据是e)添加到表头(header)之前。
         // 即,将节点添加到双向链表的末端。
         addBefore(e, header);
         return true;
     }

     public void add(int index, E element) {
         addBefore(element, (index==size ? header : entry(index)));
     }

    private Entry<E> addBefore(E e, Entry<E> entry) {
         Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
         newEntry.previous.next = newEntry;
         newEntry.next.previous = newEntry;
         size++;
         modCount++;
         return newEntry;
    }
    

(5)清除数据

public void clear(){
       Entry<E> e = header.next;
      // e可以理解为一个移动的“指针”,因为是循环链表,所以回到header的时候说明已经没有节点了
     while (e != header) {
       // 保留e的下一个节点的引用
        Entry<E> next = e.next;
        // 解除节点e对前后节点的引用
        e.next = e.previous = null;
        // 将节点e的内容置空
        e.element = null;
        // 将e移动到下一个节点
        e = next;
 }
    // 将header构造成一个循环链表,同构造方法构造一个空的LinkedList
    header.next = header.previous = header;
    // 修改size
    size = 0;
    modCount++;
}

(6)数据包含 contains(Obejct o )

public boolean contains(Object o){
    return indexOf(o) != -1;
}

public int indexOf(Object o){
    int index = 0;
    if(o == null){
        for(Entry e = header.next; e != header ; e = e->next){
            if(e.element == null)
            return index;
            index ++;
        }
    }else{
         for(Entry e = header.next; e != header ; e = e->next){
            if(o.equals(e.element))
              return index;
            index ++;
        }
    }
    return -1;
}


(7)删除数据

private E remove(Entry<E> e){
    if(e == header)
     throw new NoSuchElementException();
    // 保留将被移除的节点e的内容
    E result = e.element;
    // 将前一节点的next引用赋值为e的下一节点
    e.previous.next = e.next;
    // 将e的下一节点的previous赋值为e的上一节点
    e.next.previous = e.previous;
    // 上面两条语句的执行已经导致了无法在链表中访问到e节点,而下面解除了e节点对前后节点的引用
    e.next = e.previous = null;
    // 将被移除的节点的内容设为null
    e.element = null;     //let gc do its work
    // 修改size大小
    size--;
    modCount++;
    //返回移除节点e的内容
    return result;
}
 

(8)数据获取 get()

Get(int)方法的实现在remove(int)中已经涉及过了。首先判断位置信息是否合法(大于等于0,小于当前LinkedList实例的Size),然后遍历到具体位置,获得节点的业务数据(element)并返回。

注意:为了提高效率,需要根据获取的位置判断是从头还是从尾开始遍历。

(9)数据复制 clone() 与 toArray()

public Object clone() {
    LinkedList<E> clone = null;
    try {
        clone = (LinkedList<E>) super.clone();
    } catch (CloneNotSupportedException e) {
        throw new InternalError();
   }
    clone.header = new Entry<E>(null, null, null);
    clone.header.next = clone.header.previous = clone.header;
    clone.size = 0;
    clone.modCount = 0;
    for (Entry<E> e = header.next; e != header; e = e.next)
       clone.add(e.element);
    return clone;
}
public Object[] toArray() {
     Object[] result = new Object[size];
     int i = 0;
     for (Entry<E> e = header.next; e != header; e = e.next)
         result[i++] = e.element;
     return result;
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值