1. 程式人生 > 實用技巧 >muduo原始碼解析21-Socket類

muduo原始碼解析21-Socket類

1.基本原理

底層是一個雙向連結串列來維護資料,在JDK1.7之前是一個雙向迴圈連結串列

2.優缺點

1.優點

插入刪除效能好,容量沒有限制

可以用作記憶體佇列或棧

2.缺點

隨機訪問效能差

3.原始碼分析

1.add()方法

public boolean add(E e) {
        linkLast(e);
        return true;
    }

呼叫了linkLast方法,然後返回true

linkLast方法如下:

首先讓l指向last的位置,然後建立一個Node物件newNode,prev指標指向last,next指標指向null,元素為e,然後將last指向newNode節點,進入if判斷,如果l為null說明是第一個元素,讓first指向newNode,否則的話將newCode連結到l後面,最後進行size++

transient Node<E> first;
transient Node<E> last;

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;
        }
}

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++;
}

小結:

1.使用臨時指標l記錄最後一個元素的位子

2.建立一個新的節點,讓節點的prev指標指向l

3.再將last指標指向新的元素,如果是第一次新增,也會將first指標指向該節點

2.add(int index,E element)方法

該方法是在某個位置增加一個元素,首先判斷index是否在0-size的範圍,如果不在報出異常,然後根據index判斷,如果index==size,呼叫linkLast方法向尾節點插入元素,否則呼叫linkBefore方法插入

public void add(int index, E element) {
    checkPositionIndex(index);

    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}

首先通過node(int index)定位元素:

這裡將size處以2來和index比較,如果index在左半邊,從前開始尋找元素如果index在右半邊,從後尋找元素(二分),確認了以後,執行linkBefore方法

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;
    }
}

linkBefore方法如下:

首先將之前node定位到的元素作為succ傳入,pred指向它前一個節點,然後類似於linkLast方法插入元素

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++;
}

3.removeLast方法

public E removeLast() {
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    return unlinkLast(l);
}

使用l記錄尾節點的位置,然後呼叫unlinkLast方法:

首先通過l.prev找到前一個元素,記為prev,然後將l斷開,將last指標指向prev節點,最後返回最後一個元素

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;
}

4.removeFirst方法

public E removeFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return unlinkFirst(f);
}

使用f記錄頭節點的位置,然後呼叫unlinkFirst方法:

之後類似於上面的方法

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;
}

5.remove(int index)方法

public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}

首先通過node方法定位節點,然後呼叫unlink方法:

通過next和prev兩個指標斷開x的前後指標,將next和prev重新相接,就可以從某個位置刪除節點

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;
}