Java集合 - LinkedList

1. LinkedList概述

LinkedList 和 ArrayList 一樣,都實現(xiàn)了 List 接口宪卿,但其內(nèi)部的數(shù)據(jù)結構有本質的不同。LinkedList 是基于鏈表實現(xiàn)的万栅,所以它的插入和刪除操作比 ArrayList 更加高效佑钾。但也是由于其為基于鏈表的,所以隨機訪問的效率要比 ArrayList 差烦粒。

2. LinkedList結構

(1)LinkedList繼承關系

LinkedList類圖關系
  1. LinkedList 是一個繼承于AbstractSequentialList的雙向鏈表休溶。它也可以被當作堆棧、隊列或雙端隊列進行操作撒遣。
  2. LinkedList 實現(xiàn) List 接口邮偎,能對它進行隊列操作。
  3. LinkedList 實現(xiàn) Deque 接口义黎,即能將LinkedList當作雙端隊列使用。
  4. LinkedList 實現(xiàn)了Cloneable接口豁跑,即覆蓋了函數(shù)clone()廉涕,能克隆。
  5. LinkedList 實現(xiàn)java.io.Serializable接口艇拍,這意味著LinkedList支持序列化狐蜕,能通過序列化去傳輸。
  6. LinkedList 是非同步的卸夕。
LinkedList 為什么要繼承 AbstractSequentialList ?
  • AbstractSequentialList 實現(xiàn)了get(int index)层释、set(int index, E element)、add(int index, E element) 和 remove(int index)這些骨干性函數(shù)快集。降低了List接口的復雜度贡羔。這些接口都是隨機訪問List的廉白,LinkedList是雙向鏈表,既然它繼承于AbstractSequentialList乖寒,就相當于已經(jīng)實現(xiàn)了“get(int index)這些接口”猴蹂。
  • 此外,我們?nèi)粜枰ㄟ^AbstractSequentialList自己實現(xiàn)一個列表楣嘁,只需要擴展此類磅轻,并提供 listIterator() 和 size() 方法的實現(xiàn)即可。若要實現(xiàn)不可修改的列表逐虚,則需要實現(xiàn)列表迭代器的 hasNext聋溜、next、hasPrevious叭爱、previous 和 index 方法即可撮躁。

(2)LinkedList類定義

LinkedList中定義了三個屬性:

  • first 指向第一個節(jié)點的指針
  • last 指向最后一個節(jié)點的指針
  • size 是雙向鏈表中節(jié)點實例的個數(shù),默認為0

節(jié)點 Node :雙向鏈表中節(jié)點對應為Node類的實例涤伐。Node 中包含成員變量: prev, next, element馒胆。其中,prev是該節(jié)點的上一個節(jié)點凝果,next是該節(jié)點的下一個節(jié)點祝迂,element是該節(jié)點所包含的值。

    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
    {
    // 鏈表中節(jié)點數(shù)量 
    transient int size = 0;
    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;
    }

(3)LinkedList數(shù)據(jù)結構

LinkedList底層的數(shù)據(jù)結構是基于雙向循環(huán)鏈表的器净,且頭結點中不存放數(shù)據(jù)型雳,如下:
雙向鏈表

該雙向鏈表中每個節(jié)點實例包含三部分:業(yè)務數(shù)據(jù),前一個節(jié)點的位置信息和后一個節(jié)點位置信息山害,如下圖所示:
節(jié)點信息

對應的源碼中為Node內(nèi)部類:

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

3. LinkedList實現(xiàn)

(1)構造方法

LinkedList提供了兩個構造方法:

  • 1)無參構造的空集合:前一節(jié)點和后一節(jié)點均為null纠俭,這樣整個鏈表其實就只有header一個節(jié)點,用于表示一個空的鏈表浪慌。
    /**
     * Constructs an empty list.
     */
    public LinkedList() {
    }
  • 2)接收一個Collection參數(shù)c糟把,調(diào)用第一個構造方法構造一個空的鏈表费奸,之后通過addAll將c中的元素全部添加到鏈表中。
   /**
    * Constructs a list containing the elements of the specified
    * collection, in the order they are returned by the collection's
    * iterator.
    *
    * @param  c the collection whose elements are to be placed into this list
    * @throws NullPointerException if the specified collection is null
    */
   public LinkedList(Collection<? extends E> c) {
       this();
       addAll(c);
   }

(2)添加元素

1)add(E e)

將元素添加到鏈表的尾部,linkLast 方法分三步:
① 添加元素到尾部
② 集合長度加1(size++)
③ 集合修改次數(shù)加1(modCount++)

    /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

    /**
     * Links e as last element.
     */
    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++;
    }
2)add(int index, E element)

將元素添加到指定位置
① 檢查要添加的元素的位置是否越界
② 如果要添加的位置index=size虐急,直接在鏈表尾部添加粤蝎,等價于add(E e)方法
③ 否則蟀苛,索引到index位置的元素腺律,在其前面添加該元素

    /**
     * Inserts the specified element at the specified position in this list.
     * Shifts the element currently at that position (if any) and any
     * subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
      
        checkPositionIndex(index);

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

node(int index):二分查找某個位置的元素,如果index小于總長度size的一半古掏,從前往后查找损话,否則從后往前查找

    /**
     * Returns the (non-null) Node at the specified element index.
     */
    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;
        }
    }

找到對應位置的元素,執(zhí)行方法 linkBefore(E e, Node<E> succ) :將該節(jié)點插入到index位置的元素的前面槽唾,即調(diào)整index位置元素的頭指針指向要插入的元素丧枪,同時修改index位置前一個元素的尾指針指向新添加的元素

    /**
     * Inserts element e before non-null Node succ.
     */
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        // 將index-1位置元素存起來
        final Node<E> pred = succ.prev;
        // 創(chuàng)建要插入的新節(jié)點光涂,其頭指針指向index-1位置的元素,尾指針指向index位置元素
        final Node<E> newNode = new Node<>(pred, e, succ);
        // 調(diào)整指針位置
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

插入步驟如下:

① 找到index位置的元素
①找到index位置元素

② 創(chuàng)建要插入的節(jié)點
實例化插入的節(jié)點

③ 調(diào)整指針指向
調(diào)整指針
3)addAll(int index, Collection<? extends E> c)

將collection對象轉換成數(shù)組鏈表豪诲,插入到指定位置顶捷,同上的在指定位置插入元素類似

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

    /**
     * Inserts all of the elements in the specified collection into this
     * list, starting at the specified position.  Shifts the element
     * currently at that position (if any) and any subsequent elements to
     * the right (increases their indices).  The new elements will appear
     * in the list in the order that they are returned by the
     * specified collection's iterator.
     *
     * @param index index at which to insert the first element
     *              from the specified collection
     * @param c collection containing elements to be added to this list
     * @return {@code true} if this list changed as a result of the call
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws NullPointerException if the specified collection is null
     */
    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;
    }

(3)獲取元素

get(int index):獲取指定位置的元素
① 檢查是否越界
② 遍歷鏈表的元素

從頭開始遍歷還是從結尾開始遍歷,取決于 index 與當前鏈表長度的 size/2 比較屎篱,如果索引位置小于當前鏈表長度的一半服赎,從頭往后遍歷,否則從尾向前開始遍歷交播。
注意細節(jié):位運算與直接做除法的區(qū)別重虑。

    /**
     * Returns the element at the specified position in this list.
     *
     * @param index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

    /**
     * Returns the (non-null) Node at the specified element index.
     */
    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;
        }
    }

(4)刪除元素

1)remove()

本質是調(diào)用removeFirst()方法,刪除鏈表頭節(jié)點元素

    /**
     * Retrieves and removes the head (first element) of this list.
     *
     * @return the head of this list
     * @throws NoSuchElementException if this list is empty
     * @since 1.5
     */
    public E remove() {
        return removeFirst();
    }
2)removeFirst()

移除第一個節(jié)點秦士,將第一個節(jié)點置為null(讓GC回收)缺厉,將下一個節(jié)點變成第一個節(jié)點,鏈表長度減1隧土,修改次數(shù)加1提针。

    /**
     * Removes and returns the first element from this list.
     *
     * @return the first element from this list
     * @throws NoSuchElementException if this list is empty
     */
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

    /**
     * Unlinks non-null first node f.
     */
    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;
    }
3)removeLast()

移除最后一個節(jié)點,將最后一個節(jié)點置為null(讓GC回收)曹傀,將最后一個節(jié)點的上一節(jié)點變成最后一個節(jié)點辐脖,鏈表長度減1,修改次數(shù)加1皆愉。

    /**
     * Removes and returns the last element from this list.
     *
     * @return the last element from this list
     * @throws NoSuchElementException if this list is empty
     */
    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }

    /**
     * Unlinks non-null last node l.
     */
    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)remove(int index)

刪除指定位置元素:根據(jù)索引index獲取需要移除的節(jié)點嗜价,將移除的節(jié)點置空,調(diào)整其上一個節(jié)點和下一個節(jié)點指針的指向幕庐,鏈表長度減1久锥,修改次數(shù)加1。异剥。

    /**
     * Removes the element at the specified position in this list.  Shifts any
     * subsequent elements to the left (subtracts one from their indices).
     * Returns the element that was removed from the list.
     *
     * @param index the index of the element to be removed
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

    /**
     * Unlinks non-null node x.
     */
    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;
    }

(4)清空集合

clear() :遍歷鏈表瑟由,將所有元素置空,然后將鏈表長度修改成0冤寿,修改次數(shù)加1错妖。

    /**
     * Removes all of the elements from this list.
     * The list will be empty after this call returns.
     */
    public void clear() {
        // Clearing all of the links between nodes is "unnecessary", but:
        // - helps a generational GC if the discarded nodes inhabit
        //   more than one generation
        // - is sure to free memory even if there is a reachable Iterator
        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++;
    }

(5)包含元素

contains(Object o) :集合中是否包含某個元素
查找方式:從前向后查找,返回元素值為查找的值o的索引疚沐,不存在返回-1

indexOf(Object o)判斷鏈表中是否存在節(jié)點的element和o相等,若相等則返回該節(jié)點在鏈表中的索引位置潮模,若不存在則放回-1亮蛔。
contains(Object o)方法通過判斷indexOf(Object o)方法返回的值是否是-1來判斷鏈表中是否包含對象o。

    /**
     * Returns {@code true} if this list contains the specified element.
     * More formally, returns {@code true} if and only if this list contains
     * at least one element {@code e} such that
     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
     *
     * @param o element whose presence in this list is to be tested
     * @return {@code true} if this list contains the specified element
     */
    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }

    /**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the lowest index {@code i} such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     *
     * @param o element to search for
     * @return the index of the first occurrence of the specified element in
     *         this list, or -1 if this list does not contain the element
     */
    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;
    }

(6)復制元素

1)Object clone()

調(diào)用父類的clone()方法初始化對象鏈表clone擎厢,將clone構造成一個空的雙向循環(huán)鏈表究流,之后將first的下一個節(jié)點開始將逐個節(jié)點添加到clone中辣吃,最后返回克隆的clone對象。

    /**
     * Returns a shallow copy of this {@code LinkedList}. (The elements
     * themselves are not cloned.)
     *
     * @return a shallow copy of this {@code LinkedList} instance
     */
    public Object clone() {
        LinkedList<E> clone = superClone();

        // Put clone into "virgin" state
        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;
    }

    private LinkedList<E> superClone() {
        try {
            return (LinkedList<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }
2)Object[] toArray()

將集合轉換為數(shù)組:創(chuàng)建大小和LinkedList相等的數(shù)組result芬探,遍歷鏈表神得,將每個節(jié)點的元素element復制到數(shù)組中,返回數(shù)組偷仿。

    /**
     * Returns an array containing all of the elements in this list
     * in proper sequence (from first to last element).
     *
     * <p>The returned array will be "safe" in that no references to it are
     * maintained by this list.  (In other words, this method must allocate
     * a new array).  The caller is thus free to modify the returned array.
     *
     * <p>This method acts as bridge between array-based and collection-based
     * APIs.
     *
     * @return an array containing all of the elements in this list
     *         in proper sequence
     */
    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;
    }
3)<T> T[] toArray(T[] a)

將集合轉換為指定類型的數(shù)組:
① 先判斷數(shù)組a的大小是否足夠哩簿,若大小不夠則拓展。
② 這里用到了反射的方法酝静,重新實例化了一個大小為size的數(shù)組节榜。③ 之后將數(shù)組a賦值給數(shù)組result,遍歷鏈表向result中添加的元素别智。
④ 最后判斷數(shù)組a的長度是否大于size宗苍,若大于則將size位置的內(nèi)容設置為null,返回a薄榛。

    @SuppressWarnings("unchecked")
    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;
    }

從代碼中可以看出讳窟,數(shù)組a的length小于等于size時,a中所有元素被覆蓋敞恋,被拓展來的空間存儲的內(nèi)容都是null丽啡;若數(shù)組a的length的length大于size,則0至size-1位置的內(nèi)容被覆蓋耳舅,size位置的元素被設置為null碌上,size之后的元素不變。

(7)遍歷元素

listIterator:這是一個內(nèi)部類浦徊,用于遍歷當前的鏈表元素馏予,但是由于LinkedList也是非線程安全的類,也可能會產(chǎn)生多線程修改的異常盔性。

    public ListIterator<E> listIterator(int index) {
        checkPositionIndex(index);
        
        return new ListItr(index);
    }

    // ListItr實現(xiàn)了ListIterator接口霞丧,可知它是一個迭代器,通過它可以遍歷修改LinkedList冕香。 
    // 在LinkedList中提供了獲取ListItr對象的方法:listIterator(int index)蛹尝。
    private class ListItr implements ListIterator<E> {
        private Node<E> lastReturned;
        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;
        }
      ......
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市悉尾,隨后出現(xiàn)的幾起案子突那,更是在濱河造成了極大的恐慌,老刑警劉巖构眯,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愕难,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機猫缭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門葱弟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人猜丹,你說我怎么就攤上這事芝加。” “怎么了射窒?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵藏杖,是天一觀的道長。 經(jīng)常有香客問我轮洋,道長制市,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任弊予,我火速辦了婚禮祥楣,結果婚禮上,老公的妹妹穿的比我還像新娘汉柒。我一直安慰自己误褪,他們只是感情好,可當我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布碾褂。 她就那樣靜靜地躺著兽间,像睡著了一般。 火紅的嫁衣襯著肌膚如雪正塌。 梳的紋絲不亂的頭發(fā)上嘀略,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天,我揣著相機與錄音乓诽,去河邊找鬼帜羊。 笑死,一個胖子當著我的面吹牛鸠天,可吹牛的內(nèi)容都是我干的讼育。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼稠集,長吁一口氣:“原來是場噩夢啊……” “哼奶段!你這毒婦竟也來了?” 一聲冷哼從身側響起剥纷,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤痹籍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后晦鞋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體词裤,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡刺洒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了吼砂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鼎文,死狀恐怖渔肩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拇惋,我是刑警寧澤周偎,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站撑帖,受9級特大地震影響蓉坎,放射性物質發(fā)生泄漏。R本人自食惡果不足惜胡嘿,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一蛉艾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衷敌,春花似錦勿侯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至面氓,卻和暖如春兵钮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背舌界。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工掘譬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人禀横。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓屁药,卻偏偏與公主長得像,于是被迫代替她去往敵國和親柏锄。 傳聞我的和親對象是個殘疾皇子酿箭,可洞房花燭夜當晚...
    茶點故事閱讀 44,689評論 2 354